Various fixes, among which:

- Fixed segfault when attempting to initialseek on disconnected streams
- Fix 100% CPU bug in controller's stats code
- WebRTC UDP bind socket improvements
- Several segfault fixes
- Increased packet reordering buffer size from 30 to 150 packets
- Tweaks to default output/buffer behaviour for incoming pushes
- Added message for load balancer checks
- Fixed HLS content type
- Stats fixes
- Exit reason fixes
- Fixed socket IP address detection
- Fixed non-string arguments for stream settings
- Added caching for getConnectedBinHost()
- Added WebRTC playback rate control
- Added/completed VP8/VP9 support to WebRTC/RTSP
- Added live seek option to WebRTC
- Fixed seek to exactly newest timestamp
- Fixed HLS input

# Conflicts:
#	lib/defines.h
#	src/input/input.cpp
This commit is contained in:
Thulinma 2021-04-21 18:11:46 +02:00
parent 2b99f2f5ea
commit 0af992d405
75 changed files with 1512 additions and 790 deletions

View file

@ -77,7 +77,8 @@ namespace Mist{
option["help"] = "Generate .dtsh, then exit";
config->addOption("headeronly", option);
/*LTS-START
/*LTS-START*/
/*
//Encryption
option.null();
option["arg"] = "string";
@ -86,31 +87,6 @@ namespace Mist{
option["help"] = "a KID:KEY combo for auto-encrypting tracks";
config->addOption("encryption", option);
option.null();
option["long"] = "realtime";
option["short"] = "r";
option["help"] = "Feed the results of this input in realtime to the buffer";
config->addOption("realtime", option);
capa["optional"]["realtime"]["name"] = "Simulated Live";
capa["optional"]["realtime"]["help"] = "Make this input run as a simulated live stream";
capa["optional"]["realtime"]["option"] = "--realtime";
option.null();
option["long"] = "simulated-starttime";
option["arg"] = "integer";
option["short"] = "S";
option["help"] = "Unix timestamp on which the simulated start of the stream is based.";
option["value"].append(0);
config->addOption("simulated-starttime", option);
capa["optional"]["simulated-starttime"]["name"] = "Simulated start time";
capa["optional"]["simulated-starttime"]["help"] =
"The unix timestamp on which this stream is assumed to have started playback, or 0 for "
"automatic";
capa["optional"]["simulated-starttime"]["option"] = "--simulated-starttime";
capa["optional"]["simulated-starttime"]["type"] = "uint";
capa["optional"]["simulated-starttime"]["default"] = 0;
option.null();
option["arg"] = "string";
option["short"] = "B";
@ -175,8 +151,33 @@ namespace Mist{
capa["optional"]["playready"]["help"] = "The header to use for PlayReady encryption.";
capa["optional"]["playready"]["option"] = "--playready";
capa["optional"]["playready"]["type"] = "string";
LTS-END*/
*/
option.null();
option["long"] = "realtime";
option["short"] = "r";
option["help"] = "Feed the results of this input in realtime to the buffer";
config->addOption("realtime", option);
capa["optional"]["realtime"]["name"] = "Simulated Live";
capa["optional"]["realtime"]["help"] = "Make this input run as a simulated live stream";
capa["optional"]["realtime"]["option"] = "--realtime";
option.null();
option["long"] = "simulated-starttime";
option["arg"] = "integer";
option["short"] = "S";
option["help"] = "Unix timestamp on which the simulated start of the stream is based.";
option["value"].append(0);
config->addOption("simulated-starttime", option);
capa["optional"]["simulated-starttime"]["name"] = "Simulated start time";
capa["optional"]["simulated-starttime"]["help"] =
"The unix timestamp on which this stream is assumed to have started playback, or 0 for "
"automatic";
capa["optional"]["simulated-starttime"]["option"] = "--simulated-starttime";
capa["optional"]["simulated-starttime"]["type"] = "uint";
capa["optional"]["simulated-starttime"]["default"] = 0;
/*LTS-END*/
capa["optional"]["debug"]["name"] = "debug";
capa["optional"]["debug"]["help"] = "The debug level at which messages need to be printed.";
capa["optional"]["debug"]["option"] = "--debug";
@ -637,7 +638,7 @@ namespace Mist{
if (streamStatus){streamStatus.mapped[0] = STRMSTAT_SHUTDOWN;}
config->is_active = false;
finish();
INFO_MSG("Input for stream %s closing clean", streamName.c_str());
INFO_MSG("Input closing clean, reason: %s", Util::exitReason);
userSelect.clear();
if (streamStatus){streamStatus.mapped[0] = STRMSTAT_OFF;}
}
@ -669,6 +670,9 @@ namespace Mist{
}
}
/*LTS-END*/
if (!ret && ((Util::bootSecs() - activityCounter) >= INPUT_TIMEOUT)){
Util::logExitReason("no activity for %u seconds", Util::bootSecs() - activityCounter);
}
return ret;
}
@ -682,28 +686,29 @@ namespace Mist{
/// - call getNext() in a loop, buffering packets
void Input::stream(){
if (!config->getBool("realtime") && Util::streamAlive(streamName)){
WARN_MSG("Stream already online, cancelling");
return;
}
std::map<std::string, std::string> overrides;
overrides["throughboot"] = "";
if (isSingular()){
if (Util::streamAlive(streamName)){
WARN_MSG("Stream already online, cancelling");
return;
}
overrides["singular"] = "";
}
if (config->getBool("realtime") ||
(capa.isMember("hardcoded") && capa["hardcoded"].isMember("resume") && capa["hardcoded"]["resume"])){
overrides["resume"] = "1";
}
if (!Util::startInput(streamName, "push://INTERNAL_ONLY:" + config->getString("input"), true,
true, overrides)){// manually override stream url to start the buffer
WARN_MSG("Could not start buffer, cancelling");
return;
if (isSingular()){
if (!config->getBool("realtime") && Util::streamAlive(streamName)){
WARN_MSG("Stream already online, cancelling");
return;
}
overrides["singular"] = "";
if (!Util::startInput(streamName, "push://INTERNAL_ONLY:" + config->getString("input"), true,
true, overrides)){// manually override stream url to start the buffer
WARN_MSG("Could not start buffer, cancelling");
return;
}
}else{
if (!Util::startInput(streamName, "push://INTERNAL_PUSH:" + capa["name"].asStringRef(), true,
true, overrides)){// manually override stream url to start the buffer
WARN_MSG("Could not start buffer, cancelling");
return;
}
}
INFO_MSG("Input started");
@ -715,19 +720,23 @@ namespace Mist{
}
parseStreamHeader();
std::set<size_t> validTracks = M.getMySourceTracks(getpid());
if (!validTracks.size()){
userSelect.clear();
finish();
INFO_MSG("No tracks found, cancelling");
return;
std::set<size_t> validTracks;
if (publishesTracks()){
validTracks = M.getMySourceTracks(getpid());
if (!validTracks.size()){
userSelect.clear();
finish();
INFO_MSG("No tracks found, cancelling");
return;
}
}
timeOffset = 0;
uint64_t minFirstMs = 0;
// If resume mode is on, find matching tracks and set timeOffset values to make sure we append to the tracks.
if (config->getBool("realtime")){
if (publishesTracks() && config->getBool("realtime")){
seek(0);
minFirstMs = 0xFFFFFFFFFFFFFFFFull;
@ -736,12 +745,11 @@ namespace Mist{
uint64_t maxLastMs = 0;
// track lowest firstms value
for (std::map<unsigned int, DTSC::Track>::iterator it = myMeta.tracks.begin();
it != myMeta.tracks.end(); ++it){
if (it->second.firstms < minFirstMs){minFirstMs = it->second.firstms;}
if (it->second.firstms > maxFirstMs){maxFirstMs = it->second.firstms;}
if (it->second.lastms < minLastMs){minLastMs = it->second.lastms;}
if (it->second.lastms > maxLastMs){maxLastMs = it->second.lastms;}
for (std::set<size_t>::iterator it = validTracks.begin(); it != validTracks.end(); ++it){
if (meta.getFirstms(*it) < minFirstMs){minFirstMs = meta.getFirstms(*it);}
if (meta.getFirstms(*it) > maxFirstMs){maxFirstMs = meta.getFirstms(*it);}
if (meta.getLastms(*it) < minLastMs){minLastMs = meta.getLastms(*it);}
if (meta.getLastms(*it) > maxLastMs){maxLastMs = meta.getLastms(*it);}
}
if (maxFirstMs - minFirstMs > 500){
WARN_MSG("Begin timings of tracks for this file are %" PRIu64
@ -756,10 +764,8 @@ namespace Mist{
maxLastMs - minLastMs, minLastMs, maxLastMs);
}
// find highest current time
for (std::map<unsigned int, DTSC::Track>::iterator secondIt = tmpM.tracks.begin();
secondIt != tmpM.tracks.end(); ++secondIt){
VERYHIGH_MSG("Track %u starts at %" PRIu64, secondIt->first, secondIt->second.lastms);
timeOffset = std::max(timeOffset, (int64_t)secondIt->second.lastms);
for (std::set<size_t>::iterator it = validTracks.begin(); it != validTracks.end(); ++it){
timeOffset = std::max(timeOffset, (int64_t)meta.getLastms(*it));
}
if (timeOffset){
@ -771,13 +777,11 @@ namespace Mist{
timeOffset -= minFirstMs; // we don't need to add the lowest firstms value to the offset, as it's already there
}
}
for (std::map<unsigned int, DTSC::Track>::iterator it = myMeta.tracks.begin();
it != myMeta.tracks.end(); it++){
it->second.firstms += timeOffset;
it->second.lastms = 0;
selectedTracks.insert(it->first);
it->second.minKeepAway = SIMULATED_LIVE_BUFFER;
if (publishesTracks()){
for (std::set<size_t>::iterator it = validTracks.begin(); it != validTracks.end(); ++it){
meta.setFirstms(*it, meta.getFirstms(*it)+timeOffset);
meta.setLastms(*it, 0);
}
}
simStartTime = config->getInteger("simulated-starttime");
@ -785,9 +789,9 @@ namespace Mist{
std::string reason;
if (config->getBool("realtime")){
reason = realtimeMainLoop();
realtimeMainLoop();
}else{
reason = streamMainLoop();
streamMainLoop();
}
closeStreamSource();
@ -795,11 +799,68 @@ namespace Mist{
userSelect.clear();
finish();
INFO_MSG("Input closing clean; reason: %s", reason.c_str());
INFO_MSG("Input closing clean; reason: %s", Util::exitReason);
return;
}
std::string Input::streamMainLoop(){
void Input::streamMainLoop(){
uint64_t statTimer = 0;
uint64_t startTime = Util::bootSecs();
size_t tid;
size_t idx;
Comms::Statistics statComm;
getNext();
tid = thisPacket.getTrackId();
idx = M.trackIDToIndex(tid, getpid());
if (thisPacket && !userSelect.count(idx)){
userSelect[idx].reload(streamName, idx, COMM_STATUS_SOURCE | COMM_STATUS_DONOTTRACK);
}
while (thisPacket && config->is_active && userSelect[idx].isAlive()){
if (userSelect[idx].getStatus() == COMM_STATUS_REQDISCONNECT){
Util::logExitReason("buffer requested shutdown");
break;
}
bufferLivePacket(thisPacket);
userSelect[idx].keepAlive();
getNext();
if (!thisPacket){
Util::logExitReason("invalid packet from getNext");
break;
}
tid = thisPacket.getTrackId();
idx = M.trackIDToIndex(tid, getpid());
if (thisPacket && !userSelect.count(idx)){
userSelect[idx].reload(streamName, idx, COMM_STATUS_SOURCE | COMM_STATUS_DONOTTRACK);
}
if (Util::bootSecs() - statTimer > 1){
// Connect to stats for INPUT detection
if (!statComm){statComm.reload();}
if (statComm){
if (!statComm.isAlive()){
config->is_active = false;
Util::logExitReason("received shutdown request from controller");
return;
}
uint64_t now = Util::bootSecs();
statComm.setNow(now);
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());
statComm.keepAlive();
}
statTimer = Util::bootSecs();
}
}
}
void Input::realtimeMainLoop(){
uint64_t statTimer = 0;
uint64_t startTime = Util::bootSecs();
Comms::Statistics statComm;
@ -809,7 +870,18 @@ namespace Mist{
userSelect[tid].reload(streamName, tid, COMM_STATUS_SOURCE | COMM_STATUS_DONOTTRACK);
}
while (thisPacket && config->is_active && userSelect[thisPacket.getTrackId()].isAlive()){
thisPacket.nullMember("bpos");
while (config->is_active && userSelect[thisPacket.getTrackId()].isAlive() &&
Util::bootMS() + SIMULATED_LIVE_BUFFER < (thisPacket.getTime() + timeOffset) + simStartTime){
Util::sleep(std::min(((thisPacket.getTime() + timeOffset) + simStartTime) - (Util::getMS() + SIMULATED_LIVE_BUFFER),
(uint64_t)1000));
userSelect[thisPacket.getTrackId()].keepAlive();
}
uint64_t originalTime = thisPacket.getTime();
thisPacket.setTime(originalTime + timeOffset);
bufferLivePacket(thisPacket);
thisPacket.setTime(originalTime);
userSelect[thisPacket.getTrackId()].keepAlive();
getNext();
if (thisPacket && !userSelect.count(thisPacket.getTrackId())){
@ -823,50 +895,31 @@ namespace Mist{
if (statComm){
if (!statComm.isAlive()){
config->is_active = false;
return "received shutdown request from controller";
Util::logExitReason("received shutdown request from controller");
return;
}
uint64_t now = Util::bootSecs();
statComm.setNow(now);
statComm.setCRC(getpid());
statComm.setStream(streamName);
statComm.setConnector("INPUT");
statComm.setConnector("INPUT:" + capa["name"].asStringRef());
statComm.setUp(0);
statComm.setDown(streamByteCount());
statComm.setTime(now - startTime);
statComm.setLastSecond(0);
statComm.setHost(getConnectedBinHost());
statComm.keepAlive();
}
statTimer = Util::bootSecs();
}
}
if (!nProxy.userClient.isAlive()){return "buffer shutdown";}
if (!config->is_active){return "received deactivate signal";}
if (!thisPacket){return "Invalid packet";}
return "Unknown";
}
std::string Input::realtimeMainLoop(){
getNext();
while (thisPacket && config->is_active && nProxy.userClient.isAlive()){
thisPacket.nullMember("bpos");
while (config->is_active && nProxy.userClient.isAlive() &&
Util::bootMS() + SIMULATED_LIVE_BUFFER < (thisPacket.getTime() + timeOffset) + simStartTime){
Util::sleep(std::min(((thisPacket.getTime() + timeOffset) + simStartTime) - (Util::getMS() + SIMULATED_LIVE_BUFFER),
(uint64_t)1000));
nProxy.userClient.keepAlive();
}
uint64_t originalTime = thisPacket.getTime();
thisPacket.setTime(originalTime + timeOffset);
nProxy.bufferLivePacket(thisPacket, myMeta);
thisPacket.setTime(originalTime);
getNext();
nProxy.userClient.keepAlive();
if (!thisPacket){
Util::logExitReason("invalid packet from getNext");
}
if (thisPacket && !userSelect[thisPacket.getTrackId()].isAlive()){
Util::logExitReason("buffer shutdown");
}
if (!thisPacket){return "end of file";}
if (!config->is_active){return "received deactivate signal";}
if (!userSelect[thisPacket.getTrackId()].isAlive()){return "buffer shutdown";}
return "Unknown";
}
void Input::finish(){

View file

@ -33,6 +33,7 @@ namespace Mist{
bool hasMeta() const;
static Util::Config *config;
virtual bool needsLock(){return !config->getBool("realtime");}
virtual bool publishesTracks(){return true;}
protected:
virtual bool checkArguments() = 0;
@ -54,11 +55,12 @@ namespace Mist{
virtual void convert();
virtual void serve();
virtual void stream();
virtual std::string getConnectedBinHost(){return std::string("\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\001", 16);}
virtual size_t streamByteCount(){
return 0;
}; // For live streams: to update the stats with correct values.
virtual std::string streamMainLoop();
virtual std::string realtimeMainLoop();
virtual void streamMainLoop();
virtual void realtimeMainLoop();
bool isAlwaysOn();
virtual void userLeadIn();

View file

@ -16,6 +16,73 @@ namespace Mist{
capa["source_match"] = "balance:*";
capa["priority"] = 9;
capa["morphic"] = 1;
JSON::Value option;
option["arg"] = "integer";
option["long"] = "buffer";
option["short"] = "b";
option["help"] = "DVR buffer time in ms";
option["value"].append(50000);
config->addOption("bufferTime", option);
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 "
"available to seek around in, and will automatically be extended to fit whole keyframes as "
"well as the minimum duration needed for stable playback.";
capa["optional"]["DVR"]["option"] = "--buffer";
capa["optional"]["DVR"]["type"] = "uint";
capa["optional"]["DVR"]["default"] = 50000;
option.null();
option["arg"] = "integer";
option["long"] = "cut";
option["short"] = "c";
option["help"] = "Any timestamps before this will be cut from the live buffer";
option["value"].append(0);
config->addOption("cut", option);
capa["optional"]["cut"]["name"] = "Cut time (ms)";
capa["optional"]["cut"]["help"] =
"Any timestamps before this will be cut from the live buffer.";
capa["optional"]["cut"]["option"] = "--cut";
capa["optional"]["cut"]["type"] = "uint";
capa["optional"]["cut"]["default"] = 0;
option.null();
option["arg"] = "integer";
option["long"] = "resume";
option["short"] = "R";
option["help"] = "Enable resuming support (1) or disable resuming support (0, default)";
option["value"].append(0);
config->addOption("resume", option);
capa["optional"]["resume"]["name"] = "Resume support";
capa["optional"]["resume"]["help"] =
"If enabled, the buffer will linger after source disconnect to allow resuming the stream "
"later. If disabled, the buffer will instantly close on source disconnect.";
capa["optional"]["resume"]["option"] = "--resume";
capa["optional"]["resume"]["type"] = "select";
capa["optional"]["resume"]["select"][0u][0u] = "0";
capa["optional"]["resume"]["select"][0u][1u] = "Disabled";
capa["optional"]["resume"]["select"][1u][0u] = "1";
capa["optional"]["resume"]["select"][1u][1u] = "Enabled";
capa["optional"]["resume"]["default"] = 0;
option.null();
option["arg"] = "integer";
option["long"] = "segment-size";
option["short"] = "S";
option["help"] = "Target time duration in milliseconds for segments";
option["value"].append(5000);
config->addOption("segmentsize", option);
capa["optional"]["segmentsize"]["name"] = "Segment size (ms)";
capa["optional"]["segmentsize"]["help"] = "Target time duration in milliseconds for segments.";
capa["optional"]["segmentsize"]["option"] = "--segment-size";
capa["optional"]["segmentsize"]["type"] = "uint";
capa["optional"]["segmentsize"]["default"] = 5000;
capa["codecs"][0u][0u].append("*");
capa["codecs"][0u][1u].append("*");
capa["codecs"][0u][2u].append("*");
}
int inputBalancer::boot(int argc, char *argv[]){

View file

@ -30,6 +30,7 @@ namespace Mist{
capa["optional"].removeMember("realtime");
lastReTime = 0; /*LTS*/
finalMillis = 0;
liveMeta = 0;
capa["name"] = "Buffer";
@ -118,6 +119,7 @@ namespace Mist{
cutTime = 0;
segmentSize = 1900;
hasPush = false;
everHadPush = false;
resumeMode = false;
}
@ -360,7 +362,7 @@ namespace Mist{
if (!(users.getStatus(i) & COMM_STATUS_SOURCE)){continue;}
if (users.getTrack(i) != tid){continue;}
// We have found the right track here (pid matches, and COMM_STATUS_SOURCE set)
users.setStatus(COMM_STATUS_DISCONNECT, i);
users.setStatus(COMM_STATUS_REQDISCONNECT, i);
break;
}
@ -450,7 +452,7 @@ namespace Mist{
// firstVideo = 1 happens when there are no tracks, in which case we don't care any more
uint32_t firstKey = keys.getFirstValid();
uint32_t endKey = keys.getEndValid();
if (type != "video"){
if (type != "video" && videoFirstms != 0xFFFFFFFFFFFFFFFFull){
if ((endKey - firstKey) < 2 || keys.getTime(firstKey + 1) > videoFirstms){continue;}
}
// Buffer cutting
@ -464,19 +466,6 @@ namespace Mist{
}
}
updateMeta();
if (config->is_active){
if (streamStatus){streamStatus.mapped[0] = hasPush ? STRMSTAT_READY : STRMSTAT_WAIT;}
}
static bool everHadPush = false;
if (hasPush){
hasPush = false;
everHadPush = true;
}else if (everHadPush && !resumeMode && config->is_active){
INFO_MSG("Shutting down buffer because resume mode is disabled and the source disconnected");
if (streamStatus){streamStatus.mapped[0] = STRMSTAT_SHUTDOWN;}
config->is_active = false;
userSelect.clear();
}
}
void inputBuffer::userLeadIn(){
@ -487,22 +476,21 @@ namespace Mist{
/*LTS-END*/
connectedUsers = 0;
//Store child process PIDs in generatePids.
//These are controlled by the buffer (usually processes) and should not count towards incoming pushes
generatePids.clear();
for (std::map<std::string, pid_t>::iterator it = runningProcs.begin(); it != runningProcs.end(); it++){
generatePids.insert(it->second);
}
hasPush = false;
}
void inputBuffer::userOnActive(size_t id){
///\todo Add tracing of earliest watched keys, to prevent data going out of memory for
/// still-watching viewers
if (users.getStatus(id) != COMM_STATUS_DISCONNECT && users.getStatus(id) & COMM_STATUS_SOURCE){
sourcePids[users.getPid(id)].insert(users.getTrack(id));
if (!M.trackValid(users.getTrack(id))){
users.setStatus(COMM_STATUS_DISCONNECT, id);
return;
}
// GeneratePids holds the pids of the process that generate data, so ignore those for determining if a push is ingested.
if (!generatePids.count(users.getPid(id))){hasPush = true;}
if (M.trackValid(users.getTrack(id)) && !generatePids.count(users.getPid(id))){hasPush = true;}
}
if (!(users.getStatus(id) & COMM_STATUS_DONOTTRACK)){++connectedUsers;}
@ -516,11 +504,20 @@ namespace Mist{
}
}
void inputBuffer::userLeadOut(){
if (config->is_active && streamStatus){streamStatus.mapped[0] = hasPush ? STRMSTAT_READY : STRMSTAT_WAIT;}
if (hasPush){everHadPush = true;}
if (!hasPush && everHadPush && !resumeMode && config->is_active){
Util::logExitReason("source disconnected for non-resumable stream");
if (streamStatus){streamStatus.mapped[0] = STRMSTAT_SHUTDOWN;}
config->is_active = false;
userSelect.clear();
}
/*LTS-START*/
static std::set<size_t> prevValidTracks;
std::set<size_t> validTracks = M.getValidTracks();
if (validTracks != prevValidTracks){
MEDIUM_MSG("Valid tracks count changed from %lu to %lu", prevValidTracks.size(), validTracks.size());
prevValidTracks = validTracks;
if (Triggers::shouldTrigger("LIVE_TRACK_LIST")){
JSON::Value triggerPayload;
@ -545,7 +542,6 @@ namespace Mist{
bool inputBuffer::preRun(){
// This function gets run periodically to make sure runtime updates of the config get parsed.
lastReTime = Util::epoch(); /*LTS*/
std::string strName = config->getString("streamname");
Util::sanitizeName(strName);
strName = strName.substr(0, (strName.find_first_of("+ ")));
@ -553,16 +549,21 @@ namespace Mist{
snprintf(tmpBuf, NAME_BUFFER_SIZE, SHM_STREAM_CONF, strName.c_str());
Util::DTSCShmReader rStrmConf(tmpBuf);
DTSC::Scan streamCfg = rStrmConf.getScan();
if (streamCfg){
JSON::Value configuredProcesses = streamCfg.getMember("processes").asJSON();
checkProcesses(configuredProcesses);
}
//Check if bufferTime setting is correct
uint64_t tmpNum = retrieveSetting(streamCfg, "DVR", "bufferTime");
if (tmpNum < 1000){tmpNum = 1000;}
// if the new value is different, print a message and apply it
if (bufferTime != tmpNum){
DEVEL_MSG("Setting bufferTime from %" PRIu64 " to new value of %" PRIu64, bufferTime, tmpNum);
bufferTime = tmpNum;
}
/*LTS-START*/
//Check if cutTime setting is correct
tmpNum = retrieveSetting(streamCfg, "cut");
// if the new value is different, print a message and apply it
if (cutTime != tmpNum){
@ -570,28 +571,27 @@ namespace Mist{
cutTime = tmpNum;
}
//Check if resume setting is correct
tmpNum = retrieveSetting(streamCfg, "resume");
// if the new value is different, print a message and apply it
if (resumeMode != (bool)tmpNum){
INFO_MSG("Setting resume mode from %s to new value of %s",
resumeMode ? "enabled" : "disabled", tmpNum ? "enabled" : "disabled");
resumeMode = tmpNum;
}
if (!meta){return true;}//abort the rest if we can't write metadata
lastReTime = Util::epoch(); /*LTS*/
//Check if segmentsize setting is correct
tmpNum = retrieveSetting(streamCfg, "segmentsize");
if (M && tmpNum < M.biggestFragment() / 2){tmpNum = M.biggestFragment() / 2;}
// if the new value is different, print a message and apply it
if (tmpNum < meta.biggestFragment() / 2){tmpNum = meta.biggestFragment() / 2;}
segmentSize = meta.getMinimumFragmentDuration();
if (segmentSize != tmpNum){
INFO_MSG("Setting segmentSize from %" PRIu64 " to new value of %" PRIu64, segmentSize, tmpNum);
segmentSize = tmpNum;
if (M && M.getMinimumFragmentDuration() == 0){
meta.setMinimumFragmentDuration(segmentSize);
}
}
if (streamCfg){
JSON::Value configuredProcesses = streamCfg.getMember("processes").asJSON();
checkProcesses(configuredProcesses);
meta.setMinimumFragmentDuration(segmentSize);
}
/*LTS-END*/
return true;
}
@ -643,11 +643,8 @@ namespace Mist{
void inputBuffer::checkProcesses(const JSON::Value &procs){
if (!M.getValidTracks().size()){return;}
std::set<std::string> newProcs;
std::map<std::string, std::string> wouldSelect;
// used for building args
int zero = 0;
int out = fileno(stdout);
int err = fileno(stderr);
char *argarr[3];
@ -660,22 +657,14 @@ namespace Mist{
continue;
}
if (tmp.isMember("source_track")){
std::string sourceTrack = tmp["source_track"].asString();
if (sourceTrack != "null" && findTrack(sourceTrack) == INVALID_TRACK_ID){
// No match - skip this process
continue;
}
std::set<size_t> wouldSelect = Util::findTracks(M, JSON::Value(), "", tmp["source_track"].asStringRef());
// No match - skip this process
if (!wouldSelect.size()){continue;}
}
std::stringstream s;
if (tmp.isMember("track_select")){
std::set<size_t> wouldSelect = Util::wouldSelect(M, tmp["track_select"].asStringRef());
if (!wouldSelect.size()){
// No match - skip this process
continue;
}
for (std::set<size_t>::iterator it = wouldSelect.begin(); it != wouldSelect.end(); it++){
s << *it << " ";
}
// No match - skip this process
if (!wouldSelect.size()){continue;}
}
if (tmp.isMember("track_inhibit")){
std::set<size_t> wouldSelect = Util::wouldSelect(
@ -693,7 +682,6 @@ namespace Mist{
}
}
newProcs.insert(tmp.toString());
wouldSelect[tmp.toString()] = s.str();
}
// shut down deleted/changed processes
@ -722,8 +710,7 @@ namespace Mist{
argarr[1] = (char *)config.c_str();
argarr[2] = 0;
INFO_MSG("Starting process: %s %s", argarr[0], argarr[1]);
INFO_MSG(" WouldSelect is %s", wouldSelect.at(*newProcs.begin()).c_str());
runningProcs[*newProcs.begin()] = Util::Procs::StartPiped(argarr, &zero, &out, &err);
runningProcs[*newProcs.begin()] = Util::Procs::StartPiped(argarr, 0, 0, &err);
}
newProcs.erase(newProcs.begin());
}

View file

@ -17,7 +17,8 @@ namespace Mist{
size_t segmentSize; /*LTS*/
uint64_t lastReTime; /*LTS*/
uint64_t finalMillis;
bool hasPush;
bool hasPush;//Is a push currently being received?
bool everHadPush;//Was there ever a push received?
bool resumeMode;
IPC::semaphore *liveMeta;

View file

@ -361,7 +361,6 @@ namespace Mist{
thisPacket.reInit(srcConn); // read the next packet before continuing
continue; // parse the next packet before returning
}
thisPacket = DTSC::Packet(thisPacket, M.trackIDToIndex(thisPacket.getTrackId(), getpid()));
return; // we have a packet
}
}

View file

@ -27,6 +27,11 @@ namespace Mist{
inputDTSC(Util::Config *cfg);
bool needsLock();
virtual std::string getConnectedBinHost(){
if (srcConn){return srcConn.getBinHost();}
return Input::getConnectedBinHost();
}
protected:
// Private Functions
bool openStreamSource();

View file

@ -114,12 +114,17 @@ namespace Mist{
while (ptr.size() < needed){
if (!ptr.allocate(needed)){return false;}
int64_t toRead = needed - ptr.size();
if (!fread(ptr + ptr.size(), toRead, 1, inFile)){
// We assume if there is no current data buffered, that we are at EOF and don't print a warning
if (ptr.size()){
FAIL_MSG("Could not read more data! (have %zu, need %" PRIu32 ")", ptr.size(), needed);
int readResult = 0;
while (!readResult){
readResult = fread(ptr + ptr.size(), toRead, 1, inFile);
if (!readResult){
if (errno == EINTR){continue;}
// At EOF we don't print a warning
if (!feof(inFile)){
FAIL_MSG("Could not read more data! (have %zu, need %" PRIu32 ")", ptr.size(), needed);
}
return false;
}
return false;
}
totalBytes += toRead;
ptr.size() = needed;
@ -463,7 +468,7 @@ namespace Mist{
}break;
}
}
thisPacket.genericFill(C.time, C.offset, M.trackIDToIndex(C.track, getpid()), C.ptr, C.dsize,
thisPacket.genericFill(C.time, C.offset, C.track, C.ptr, C.dsize,
C.bpos, C.key);
}

View file

@ -525,8 +525,7 @@ namespace Mist{
std::string test = root.link(entry.filename).getFilePath();
fileSource.open(test.c_str(), std::ios::ate | std::ios::binary);
if (!fileSource.good()){WARN_MSG("file: %s, error: %s", test.c_str(), strerror(errno));}
entry.byteEnd = fileSource.tellg();
totalBytes += entry.byteEnd;
totalBytes += fileSource.tellg();
}
entry.timestamp = lastTimestamp + startTime;
@ -592,12 +591,9 @@ namespace Mist{
void inputHLS::parseStreamHeader(){
if (!initPlaylist(config->getString("input"))){
FAIL_MSG("Failed to load HLS playlist, aborting");
myMeta = DTSC::Meta();
return;
}
myMeta = DTSC::Meta();
myMeta.live = true;
myMeta.vod = false;
meta.reInit(config->getString("streamname"), false);
INFO_MSG("Parsing live stream to create header...");
TS::Packet packet; // to analyse and extract data
int counter = 1;
@ -612,7 +608,7 @@ namespace Mist{
for (std::deque<playListEntries>::iterator entryIt = pListIt->second.begin();
entryIt != pListIt->second.end(); ++entryIt){
nProxy.userClient.keepAlive();
keepAlive();
if (!segDowner.loadSegment(*entryIt)){
WARN_MSG("Skipping segment that could not be loaded in an attempt to recover");
tsStream.clear();
@ -633,21 +629,24 @@ namespace Mist{
tsStream.getEarliestPacket(headerPack);
int tmpTrackId = headerPack.getTrackId();
uint64_t packetId = pidMapping[(((uint64_t)pListIt->first) << 32) + tmpTrackId];
if (packetId == 0){
pidMapping[(((uint64_t)pListIt->first) << 32) + headerPack.getTrackId()] = counter;
pidMappingR[counter] = (((uint64_t)pListIt->first) << 32) + headerPack.getTrackId();
packetId = counter;
VERYHIGH_MSG("Added file %s, trackid: %zu, mapped to: %d",
entryIt->filename.c_str(), headerPack.getTrackId(), counter);
VERYHIGH_MSG("Added file %s, trackid: %zu, mapped to: %d", entryIt->filename.c_str(),
headerPack.getTrackId(), counter);
counter++;
}
if ((!myMeta.tracks.count(packetId) || !myMeta.tracks[packetId].codec.size())){
tsStream.initializeMetadata(myMeta, tmpTrackId, packetId);
myMeta.tracks[packetId].minKeepAway = globalWaitTime * 2000;
VERYHIGH_MSG("setting minKeepAway = %d for track: %" PRIu64,
myMeta.tracks[packetId].minKeepAway, packetId);
size_t idx = M.trackIDToIndex(packetId, getpid());
if ((idx == INVALID_TRACK_ID || !M.getCodec(idx).size())){
tsStream.initializeMetadata(meta, tmpTrackId, packetId);
idx = M.trackIDToIndex(packetId, getpid());
if (idx != INVALID_TRACK_ID){
meta.setMinKeepAway(idx, globalWaitTime * 2000);
VERYHIGH_MSG("setting minKeepAway = %" PRIu32 " for track: %zu", globalWaitTime * 2000, idx);
}
}
}
break; // we have all tracks discovered, next playlist!
@ -655,8 +654,6 @@ namespace Mist{
}while (!segDowner.atEnd());
if (preCounter < counter){break;}// We're done reading this playlist!
}
in.close();
}
tsStream.clear();
currentPlaylist = 0;
@ -673,8 +670,6 @@ namespace Mist{
meta.reInit(config->getString("streamname"), config->getString("input") + ".dtsh");
hasHeader = (bool)M;
if (M){return true;}
if (!hasHeader){meta.reInit(config->getString("streamname"), true);}
TS::Packet packet; // to analyse and extract data
@ -704,7 +699,7 @@ namespace Mist{
DTSC::Packet headerPack;
tsStream.getEarliestPacket(headerPack);
int tmpTrackId = headerPack.getTrackId();
size_t tmpTrackId = headerPack.getTrackId();
uint64_t packetId = pidMapping[(((uint64_t)pListIt->first) << 32) + tmpTrackId];
if (packetId == 0){
@ -717,10 +712,8 @@ namespace Mist{
}
size_t idx = M.trackIDToIndex(packetId, getpid());
INFO_MSG("PacketID: %" PRIu64 ", pid: %d, mapped to %zu", packetId, getpid(), idx);
if (!hasHeader && (idx == INVALID_TRACK_ID || !M.getCodec(idx).size())){
tsStream.initializeMetadata(meta, tmpTrackId, packetId);
INFO_MSG("InitializingMeta for track %zu -> %zu", tmpTrackId, packetId);
idx = M.trackIDToIndex(packetId, getpid());
}
@ -757,6 +750,7 @@ namespace Mist{
counter++;
}
size_t idx = M.trackIDToIndex(packetId, getpid());
if (!hasHeader && (idx == INVALID_TRACK_ID || !M.getCodec(idx).size())){
tsStream.initializeMetadata(meta, tmpTrackId, packetId);
idx = M.trackIDToIndex(packetId, getpid());
@ -781,7 +775,6 @@ namespace Mist{
INFO_MSG("write header file...");
M.toFile((config->getString("input") + ".dtsh").c_str());
in.close();
return true;
}
@ -794,26 +787,29 @@ namespace Mist{
INSANE_MSG("Getting next");
uint32_t tid = 0;
bool finished = false;
if (userSelect.size()){tid = userSelect.begin()->first;}
thisPacket.null();
while (config->is_active && (needsLock() || keepAlive())){
// Check if we have a packet
bool hasPacket = false;
if (streamIsLive){
if (idx == INVALID_TRACK_ID){
hasPacket = tsStream.hasPacketOnEachTrack() || (segDowner.atEnd() && tsStream.hasPacket());
}else{
hasPacket = tsStream.hasPacket(M.getID(idx) & 0xFFFF);
hasPacket = tsStream.hasPacket(getMappedTrackId(M.getID(idx)));
}
// Yes? Excellent! Read and return it.
if (hasPacket){
// Read
if (M.getLive()){
if (idx == INVALID_TRACK_ID){
tsStream.getEarliestPacket(thisPacket);
tid = M.trackIDToIndex((((uint64_t)currentPlaylist) << 16) + thisPacket.getTrackId(), getpid());
tid = getOriginalTrackId(currentPlaylist, thisPacket.getTrackId());
if (!tid){
INFO_MSG("Track %" PRIu64 " on PLS %" PRIu64 " -> %" PRIu32, thisPacket.getTrackId(), currentPlaylist, tid);
continue;
}
}else{
tsStream.getPacket(M.getID(idx) & 0xFFFF, thisPacket);
tsStream.getPacket(getMappedTrackId(M.getID(idx)), thisPacket);
}
if (!thisPacket){
FAIL_MSG("Could not getNext TS packet!");
@ -850,8 +846,8 @@ namespace Mist{
plsTimeOffset[currentPlaylist] +=
(int64_t)(plsLastTime[currentPlaylist] + plsInterval[currentPlaylist]) - (int64_t)newTime;
newTime = thisPacket.getTime() + plsTimeOffset[currentPlaylist];
INFO_MSG("[Guess; New offset: %" PRId64 " -> %" PRId64 "] Packet %" PRIu32
"@%" PRIu64 "ms -> %" PRIu64 "ms",
INFO_MSG("[Guess; New offset: %" PRId64 " -> %" PRId64 "] Packet %" PRIu32 "@%" PRIu64
"ms -> %" PRIu64 "ms",
prevOffset, plsTimeOffset[currentPlaylist], tid, thisPacket.getTime(), newTime);
}
}
@ -891,24 +887,28 @@ namespace Mist{
// No? Then we want to try reading the next file.
// No segments? Wait until next playlist reloading time.
currentPlaylist = firstSegment();
if (idx != INVALID_TRACK_ID){
currentPlaylist = getMappedTrackPlaylist(M.getID(idx));
}else{
currentPlaylist = firstSegment();
}
if (currentPlaylist < 0){
VERYHIGH_MSG("Waiting for segments...");
if (nProxy.userClient.isAlive()){nProxy.userClient.keepAlive();}
keepAlive();
Util::wait(500);
continue;
}
// Now that we know our playlist is up-to-date, actually try to read the file.
VERYHIGH_MSG("Moving on to next TS segment (variant %" PRIu32 ")", currentPlaylist);
VERYHIGH_MSG("Moving on to next TS segment (variant %" PRIu64 ")", currentPlaylist);
if (readNextFile()){
MEDIUM_MSG("Next segment read successfully");
finished = false;
continue; // Success! Continue regular parsing.
}else{
if (selectedTracks.size() > 1){
if (userSelect.size() > 1){
// failed to read segment for playlist, dropping it
WARN_MSG("Dropping variant %" PRIu32 " because we couldn't read anything from it", currentPlaylist);
WARN_MSG("Dropping variant %" PRIu64 " because we couldn't read anything from it", currentPlaylist);
tthread::lock_guard<tthread::mutex> guard(entryMutex);
listEntries.erase(currentPlaylist);
if (listEntries.size()){continue;}
@ -946,17 +946,17 @@ namespace Mist{
currentIndex = plistEntry - 1;
currentPlaylist = getMappedTrackPlaylist(trackId);
INFO_MSG("Seeking to index %d on playlist %d", currentIndex, currentPlaylist);
INFO_MSG("Seeking to index %zu on playlist %" PRIu64, currentIndex, currentPlaylist);
{// Lock mutex for listEntries
tthread::lock_guard<tthread::mutex> guard(entryMutex);
if (!listEntries.count(currentPlaylist)){
WARN_MSG("Playlist %d not loaded, aborting seek", currentPlaylist);
WARN_MSG("Playlist %" PRIu64 " not loaded, aborting seek", currentPlaylist);
return;
}
std::deque<playListEntries> &curPlaylist = listEntries[currentPlaylist];
if (curPlaylist.size() <= currentIndex){
WARN_MSG("Playlist %d has %zu <= %d entries, aborting seek", currentPlaylist,
WARN_MSG("Playlist %" PRIu64 " has %zu <= %zu entries, aborting seek", currentPlaylist,
curPlaylist.size(), currentIndex);
return;
}
@ -1179,7 +1179,7 @@ namespace Mist{
tthread::lock_guard<tthread::mutex> guard(entryMutex);
std::deque<playListEntries> &curList = listEntries[currentPlaylist];
if (!curList.size()){
WARN_MSG("no entries found in playlist: %d!", currentPlaylist);
WARN_MSG("no entries found in playlist: %" PRIu64 "!", currentPlaylist);
return false;
}
if (!streamIsLive){
@ -1204,7 +1204,7 @@ namespace Mist{
if (Util::bootSecs() < ntry.timestamp){
VERYHIGH_MSG("Slowing down to realtime...");
while (Util::bootSecs() < ntry.timestamp){
if (nProxy.userClient.isAlive()){nProxy.userClient.keepAlive();}
keepAlive();
Util::wait(250);
}
}
@ -1228,7 +1228,7 @@ namespace Mist{
/// this will keep the playlists in sync while reading segments.
size_t inputHLS::firstSegment(){
// Only one selected? Immediately return the right playlist.
if (userSelect.size() == 1){return ((M.getID(userSelect.begin()->first) >> 16) & 0xFFFF);}
if (userSelect.size() == 1){return getMappedTrackPlaylist(M.getID(userSelect.begin()->first));}
uint64_t firstTimeStamp = 0;
int tmpId = -1;
int segCount = 0;

View file

@ -121,7 +121,7 @@ namespace Mist{
Socket::Connection conn;
TS::Packet tsBuf;
int firstSegment();
size_t firstSegment();
void waitForNextSegment();
void readPMT();
bool checkArguments();
@ -130,7 +130,6 @@ namespace Mist{
bool needHeader(){return true;}
void getNext(size_t idx = INVALID_TRACK_ID);
void seek(uint64_t seekTime, size_t idx = INVALID_TRACK_ID);
FILE *inFile;
FILE *tsFile;
@ -141,6 +140,9 @@ namespace Mist{
void parseStreamHeader();
uint32_t getMappedTrackId(uint64_t id);
uint32_t getMappedTrackPlaylist(uint64_t id);
uint64_t getOriginalTrackId(uint32_t playlistId, uint32_t id);
size_t getEntryId(uint32_t playlistId, uint64_t bytePos);
};
}// namespace Mist

View file

@ -39,17 +39,19 @@ namespace Mist{
return true;
}
std::string inputPlaylist::streamMainLoop(){
void inputPlaylist::streamMainLoop(){
bool seenValidEntry = true;
uint64_t startTime = Util::bootMS();
while (config->is_active && nProxy.userClient.isAlive()){
while (config->is_active){
struct tm *wTime;
time_t nowTime = time(0);
wTime = localtime(&nowTime);
wallTime = wTime->tm_hour * 60 + wTime->tm_min;
nProxy.userClient.keepAlive();
reloadPlaylist();
if (!playlist.size()){return "No entries in playlist";}
if (!playlist.size()){
Util::logExitReason("No entries in playlist");
return;
}
++playlistIndex;
if (playlistIndex >= playlist.size()){
if (!seenValidEntry){
@ -103,7 +105,7 @@ namespace Mist{
continue;
}
seenValidEntry = true;
while (Util::Procs::isRunning(spawn_pid) && nProxy.userClient.isAlive() && config->is_active){
while (Util::Procs::isRunning(spawn_pid) && config->is_active){
Util::sleep(1000);
if (reloadOn != 0xFFFF){
time_t nowTime = time(0);
@ -117,13 +119,9 @@ namespace Mist{
Util::Procs::Stop(spawn_pid);
}
}
nProxy.userClient.keepAlive();
}
if (!config->is_active && Util::Procs::isRunning(spawn_pid)){Util::Procs::Stop(spawn_pid);}
}
if (!config->is_active){return "received deactivate signal";}
if (!nProxy.userClient.isAlive()){return "buffer shutdown";}
return "Unknown";
}
void inputPlaylist::reloadPlaylist(){

View file

@ -11,9 +11,10 @@ namespace Mist{
protected:
bool checkArguments();
bool readHeader(){return true;}
virtual void parseStreamHeader(){myMeta.tracks[1].codec = "PLACEHOLDER";}
std::string streamMainLoop();
virtual void parseStreamHeader(){}
void streamMainLoop();
virtual bool needHeader(){return false;}
virtual bool publishesTracks(){return false;}
private:
void reloadPlaylist();

View file

@ -45,6 +45,8 @@ namespace Mist{
capa["codecs"][0u][0u].append("H264");
capa["codecs"][0u][0u].append("HEVC");
capa["codecs"][0u][0u].append("MPEG2");
capa["codecs"][0u][0u].append("VP8");
capa["codecs"][0u][0u].append("VP9");
capa["codecs"][0u][1u].append("AAC");
capa["codecs"][0u][1u].append("MP3");
capa["codecs"][0u][1u].append("AC3");
@ -194,45 +196,45 @@ namespace Mist{
tcpCon.close();
}
std::string InputRTSP::streamMainLoop(){
IPC::sharedClient statsPage = IPC::sharedClient(SHM_STATISTICS, STAT_EX_SIZE, true);
void InputRTSP::streamMainLoop(){
Comms::Statistics statComm;
uint64_t startTime = Util::epoch();
uint64_t lastPing = Util::bootSecs();
uint64_t lastSecs = 0;
while (keepAlive() && parsePacket()){
uint64_t currSecs = Util::bootSecs();
handleUDP();
if (Util::bootSecs() - lastPing > 30){
sendCommand("GET_PARAMETER", url.getUrl(), "");
lastPing = Util::bootSecs();
}
if (lastSecs != currSecs){
if (!statsPage.getData()){
statsPage = IPC::sharedClient(SHM_STATISTICS, STAT_EX_SIZE, true);
}
if (statsPage.getData()){
if (!statsPage.isAlive()){
lastSecs = currSecs;
// Connect to stats for INPUT detection
statComm.reload();
if (statComm){
if (!statComm.isAlive()){
config->is_active = false;
statsPage.finish();
return "received shutdown request from controller";
Util::logExitReason("received shutdown request from controller");
return;
}
uint64_t now = Util::epoch();
IPC::statExchange tmpEx(statsPage.getData());
tmpEx.now(now);
tmpEx.crc(getpid());
tmpEx.streamName(streamName);
tmpEx.connector("INPUT");
tmpEx.up(tcpCon.dataUp());
tmpEx.down(tcpCon.dataDown());
tmpEx.time(now - startTime);
tmpEx.lastSecond(0);
statsPage.keepAlive();
uint64_t now = Util::bootSecs();
statComm.setNow(now);
statComm.setCRC(getpid());
statComm.setStream(streamName);
statComm.setConnector("INPUT:" + capa["name"].asStringRef());
statComm.setUp(tcpCon.dataUp());
statComm.setDown(tcpCon.dataDown());
statComm.setTime(now - startTime);
statComm.setLastSecond(0);
statComm.setHost(getConnectedBinHost());
statComm.keepAlive();
}
}
}
statsPage.finish();
if (!tcpCon){return "TCP connection closed";}
if (!config->is_active){return "received deactivate signal";}
if (!keepAlive()){return "buffer shutdown";}
return "Unknown";
if (!tcpCon){
Util::logExitReason("TCP connection closed");
}
}
bool InputRTSP::parsePacket(bool mustHave){

View file

@ -17,6 +17,11 @@ namespace Mist{
void incoming(const DTSC::Packet &pkt);
void incomingRTP(const uint64_t track, const RTP::Packet &p);
virtual std::string getConnectedBinHost(){
if (tcpCon){return tcpCon.getBinHost();}
return Input::getConnectedBinHost();
}
protected:
// Private Functions
bool checkArguments();
@ -29,7 +34,7 @@ namespace Mist{
const std::map<std::string, std::string> *extraHeaders = 0, bool reAuth = true);
bool parsePacket(bool mustHave = false);
bool handleUDP();
std::string streamMainLoop();
void streamMainLoop();
Socket::Connection tcpCon;
HTTP::Parser sndH, recH;
HTTP::URL url;

View file

@ -463,7 +463,7 @@ namespace Mist{
tmpIdx = meta.addTrack(0, 0, 0, 0);
}
std::string inputTS::streamMainLoop(){
void inputTS::streamMainLoop(){
meta.removeTrack(tmpIdx);
INFO_MSG("Removed temptrack %zu", tmpIdx);
Comms::Statistics statComm;
@ -495,7 +495,8 @@ namespace Mist{
}
if (!tcpCon){
config->is_active = false;
return "end of streamed input";
Util::logExitReason("end of streamed input");
return;
}
}else{
std::string leftData;
@ -557,17 +558,19 @@ namespace Mist{
if (statComm){
if (!statComm.isAlive()){
config->is_active = false;
return "received shutdown request from controller";
Util::logExitReason("received shutdown request from controller");
return;
}
uint64_t now = Util::bootSecs();
statComm.setNow(now);
statComm.setCRC(getpid());
statComm.setStream(streamName);
statComm.setConnector("INPUT");
statComm.setConnector("INPUT:" + capa["name"].asStringRef());
statComm.setUp(0);
statComm.setDown(downCounter + tcpCon.dataDown());
statComm.setTime(now - startTime);
statComm.setLastSecond(0);
statComm.setHost(getConnectedBinHost());
statComm.keepAlive();
}
@ -577,7 +580,8 @@ namespace Mist{
if (hasStarted && !threadTimer.size()){
if (!isAlwaysOn()){
config->is_active = false;
return "no active threads and we had input in the past";
Util::logExitReason("no active threads and we had input in the past");
return;
}else{
hasStarted = false;
}
@ -607,13 +611,13 @@ namespace Mist{
if (Util::bootSecs() - noDataSince > 20){
if (!isAlwaysOn()){
config->is_active = false;
return "No packets received for 20 seconds - terminating";
Util::logExitReason("no packets received for 20 seconds");
return;
}else{
noDataSince = Util::bootSecs();
}
}
}
return "received shutdown request";
}
void inputTS::finish(){

View file

@ -14,6 +14,11 @@ namespace Mist{
~inputTS();
bool needsLock();
virtual std::string getConnectedBinHost(){
if (tcpCon){return tcpCon.getBinHost();}
/// \TODO Handle UDP
return Input::getConnectedBinHost();
}
protected:
// Private Functions
bool checkArguments();
@ -25,7 +30,7 @@ namespace Mist{
void readPMT();
bool openStreamSource();
void parseStreamHeader();
std::string streamMainLoop();
void streamMainLoop();
void finish();
FILE *inFile; ///< The input file with ts data
TS::Stream tsStream; ///< Used for parsing the incoming ts stream