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

@ -343,7 +343,7 @@ int main_loop(int argc, char **argv){
WARN_MSG("You have very little free RAM available (%" PRIu64
" MiB). While Mist will run just fine with this amount, do note that random crashes "
"may occur should you ever run out of free RAM. Please be pro-active and keep an "
"eye on the RAM usage!");
"eye on the RAM usage!", (mem_free + mem_bufcache)/1024);
}
if (shm_free < 1024 * 1024 && mem_total > 1024 * 1024 * 1.12){
WARN_MSG("You have very little shared memory available (%" PRIu64

View file

@ -502,6 +502,12 @@ void Controller::handleAPICommands(JSON::Value &Request, JSON::Value &Response){
Controller::triggerStats[Request["trigger_fail"].asStringRef()].failCount++;
return;
}
if (Request.isMember("push_status_update")){
JSON::Value &statUp = Request["push_status_update"];
if (statUp.isMember("id") && statUp.isMember("status")){
setPushStatus(statUp["id"].asInt(), statUp["status"]);
}
}
/*LTS-END*/
// Parse config and streams from the request.
if (Request.isMember("config") && Request["config"].isObject()){

View file

@ -200,6 +200,12 @@ namespace Controller{
trgs["DEFAULT_STREAM"]["response_action"] =
"Overrides the default stream setting (for this view) to the response value. If empty, "
"fails loading the stream and returns an error to the viewer/user.";
trgs["PUSH_END"]["when"] = "Every time a push stops, for any reason";
trgs["PUSH_END"]["stream_specific"] = true;
trgs["PUSH_END"]["payload"] = "push ID (integer)\nstream name (string)\ntarget URI, before variables/triggers affected it (string)\ntarget URI, afterwards, as actually used (string)\nlast 10 log messages (JSON array string)\nmost recent push status (JSON object string)";
trgs["PUSH_END"]["response"] = "ignored";
trgs["PUSH_END"]["response_action"] = "None.";
}
/// Aquire list of available protocols, storing in global 'capabilities' JSON::Value.

View file

@ -164,8 +164,6 @@ namespace Controller{
std::set<std::string> runningConns;
// used for building args
int zero = 0;
int out = fileno(stdout);
int err = fileno(stderr);
char *argarr[15]; // approx max # of args (with a wide margin)
int i;
@ -259,7 +257,7 @@ namespace Controller{
JSON::Value p = JSON::fromString(*runningConns.begin());
buildPipedArguments(p, (char **)&argarr, capabilities);
// start piped w/ generated args
currentConnectors[*runningConns.begin()] = Util::Procs::StartPiped(argarr, &zero, &out, &err);
currentConnectors[*runningConns.begin()] = Util::Procs::StartPiped(argarr, 0, 0, &err);
Triggers::doTrigger("OUTPUT_START", *runningConns.begin()); // LTS
}
runningConns.erase(runningConns.begin());

View file

@ -7,6 +7,7 @@
#include <mist/procs.h>
#include <mist/stream.h>
#include <mist/tinythread.h>
#include <mist/triggers.h>
#include <string>
namespace Controller{
@ -38,6 +39,38 @@ namespace Controller{
}
}
void setPushStatus(uint64_t id, const JSON::Value & status){
if (!activePushes.count(id)){return;}
activePushes[id][5].extend(status);
}
void pushLogMessage(uint64_t id, const JSON::Value & msg){
JSON::Value &log = activePushes[id][4];
log.append(msg);
log.shrink(10);
}
bool isPushActive(uint64_t id){
while (Controller::conf.is_active && !pushListRead){Util::sleep(100);}
return activePushes.count(id);
}
/// Only used internally, to remove pushes
static void removeActivePush(pid_t id){
//ignore if the push does not exist
if (!activePushes.count(id)){return;}
JSON::Value p = activePushes[id];
if (Triggers::shouldTrigger("PUSH_END", p[1].asStringRef())){
std::string payload = p[0u].asString() + "\n" + p[1u].asString() + "\n" + p[2u].asString() + "\n" + p[3u].asString() + "\n" + p[4u].toString() + "\n" + p[5u].toString();
Triggers::doTrigger("PUSH_END", payload, p[1].asStringRef());
}
//actually remove, make sure next pass the new list is written out too
activePushes.erase(id);
mustWritePushList = true;
}
/// Returns true if the push is currently active, false otherwise.
bool isPushActive(const std::string &streamname, const std::string &target){
while (Controller::conf.is_active && !pushListRead){Util::sleep(100);}
@ -52,8 +85,7 @@ namespace Controller{
}
}
while (toWipe.size()){
activePushes.erase(*toWipe.begin());
mustWritePushList = true;
removeActivePush(*toWipe.begin());
toWipe.erase(toWipe.begin());
}
return false;
@ -75,8 +107,7 @@ namespace Controller{
}
}
while (toWipe.size()){
activePushes.erase(*toWipe.begin());
mustWritePushList = true;
removeActivePush(*toWipe.begin());
toWipe.erase(toWipe.begin());
}
}
@ -198,6 +229,17 @@ namespace Controller{
break;
}
}
//Check if any pushes have ended, clean them up
std::set<pid_t> toWipe;
for (std::map<pid_t, JSON::Value>::iterator it = activePushes.begin(); it != activePushes.end(); ++it){
if (!Util::Procs::isActive(it->first)){toWipe.insert(it->first);}
}
while (toWipe.size()){
removeActivePush(*toWipe.begin());
toWipe.erase(toWipe.begin());
mustWritePushList = true;
}
//write push list to shared memory, for restarting/crash recovery/etc
if (mustWritePushList && pushPage.mapped){
writePushList(pushPage.mapped);
mustWritePushList = false;
@ -227,8 +269,7 @@ namespace Controller{
}
}
while (toWipe.size()){
activePushes.erase(*toWipe.begin());
mustWritePushList = true;
removeActivePush(*toWipe.begin());
toWipe.erase(toWipe.begin());
}
}

View file

@ -8,6 +8,9 @@ namespace Controller{
void startPush(const std::string &streamname, std::string &target);
void stopPush(unsigned int ID);
void listPush(JSON::Value &output);
void pushLogMessage(uint64_t id, const JSON::Value & msg);
void setPushStatus(uint64_t id, const JSON::Value & status);
bool isPushActive(uint64_t id);
// Functions for automated pushes, add/remove
void addPush(JSON::Value &request);

View file

@ -50,6 +50,7 @@ bool Controller::killOnExit = KILL_ON_EXIT;
tthread::mutex Controller::statsMutex;
unsigned int Controller::maxConnsPerIP = 0;
uint64_t Controller::statDropoff = 0;
static uint64_t cpu_use = 0;
char noBWCountMatches[1717];
uint64_t bwLimit = 128 * 1024 * 1024; // gigabit default limit
@ -76,6 +77,21 @@ void Controller::updateBandwidthConfig(){
}
}
}
//Localhost is always excepted from counts
{
std::string newbins = Socket::getBinForms("::1");
if (offset + newbins.size() < 1700){
memcpy(noBWCountMatches + offset, newbins.data(), newbins.size());
offset += newbins.size();
}
}
{
std::string newbins = Socket::getBinForms("127.0.0.1/8");
if (offset + newbins.size() < 1700){
memcpy(noBWCountMatches + offset, newbins.data(), newbins.size());
offset += newbins.size();
}
}
}
// For server-wide totals. Local to this file only.
@ -107,7 +123,7 @@ Controller::sessIndex::sessIndex(){
/// into strings. This extracts the host, stream name, connector and crc field, ignoring everything
/// else.
Controller::sessIndex::sessIndex(const Comms::Statistics &statComm, size_t id){
host = statComm.getHost(id);
Socket::hostBytesToStr(statComm.getHost(id).data(), 16, host);
streamName = statComm.getStream(id);
connector = statComm.getConnector(id);
crc = statComm.getCRC(id);
@ -336,6 +352,28 @@ void Controller::SharedMemStats(void *config){
bool firstRun = true;
while (((Util::Config *)config)->is_active){
{
std::ifstream cpustat("/proc/stat");
if (cpustat){
char line[300];
while (cpustat.getline(line, 300)){
static unsigned long long cl_total = 0, cl_idle = 0;
unsigned long long c_user, c_nice, c_syst, c_idle, c_total;
if (sscanf(line, "cpu %Lu %Lu %Lu %Lu", &c_user, &c_nice, &c_syst, &c_idle) == 4){
c_total = c_user + c_nice + c_syst + c_idle;
if (c_total > cl_total){
cpu_use = (long long)(1000 - ((c_idle - cl_idle) * 1000) / (c_total - cl_total));
}else{
cpu_use = 0;
}
cl_total = c_total;
cl_idle = c_idle;
break;
}
}
}
}
{
tthread::lock_guard<tthread::mutex> guard(Controller::configMutex);
tthread::lock_guard<tthread::mutex> guard2(statsMutex);
cacheLock->wait(); /*LTS*/
@ -522,7 +560,8 @@ uint32_t Controller::statSession::kill(){
/// Updates the given active connection with new stats data.
void Controller::statSession::update(uint64_t index, Comms::Statistics &statComm){
std::string myHost = statComm.getHost(index);
std::string myHost;
Socket::hostBytesToStr(statComm.getHost(index).data(), 16, myHost);
std::string myStream = statComm.getStream(index);
std::string myConnector = statComm.getConnector(index);
// update the sync byte: 0 = requesting fill, 2 = requesting refill, 1 = needs checking, > 2 =
@ -613,12 +652,12 @@ void Controller::statSession::update(uint64_t index, Comms::Statistics &statComm
}
if (currDown + currUp >= COUNTABLE_BYTES){
if (sessionType == SESS_UNSET){
if (myConnector == "INPUT"){
if (myConnector.size() >= 5 && myConnector.substr(0, 5) == "INPUT"){
++servInputs;
streamStats[myStream].inputs++;
streamStats[myStream].currIns++;
sessionType = SESS_INPUT;
}else if (myConnector == "OUTPUT"){
}else if (myConnector.size() >= 6 && myConnector.substr(0, 6) == "OUTPUT"){
++servOutputs;
streamStats[myStream].outputs++;
streamStats[myStream].currOuts++;
@ -632,19 +671,21 @@ void Controller::statSession::update(uint64_t index, Comms::Statistics &statComm
}
// If previous < COUNTABLE_BYTES, we haven't counted any data so far.
// We need to count all the data in that case, otherwise we only count the difference.
if (prevUp + prevDown < COUNTABLE_BYTES){
if (!myStream.size() || myStream[0] == 0){
if (streamStats.count(myStream)){streamStats.erase(myStream);}
if (noBWCount != 2){ //only count connections that are countable
if (prevUp + prevDown < COUNTABLE_BYTES){
if (!myStream.size() || myStream[0] == 0){
if (streamStats.count(myStream)){streamStats.erase(myStream);}
}else{
streamStats[myStream].upBytes += currUp;
streamStats[myStream].downBytes += currDown;
}
}else{
streamStats[myStream].upBytes += currUp;
streamStats[myStream].downBytes += currDown;
}
}else{
if (!myStream.size() || myStream[0] == 0){
if (streamStats.count(myStream)){streamStats.erase(myStream);}
}else{
streamStats[myStream].upBytes += currUp - prevUp;
streamStats[myStream].downBytes += currDown - prevDown;
if (!myStream.size() || myStream[0] == 0){
if (streamStats.count(myStream)){streamStats.erase(myStream);}
}else{
streamStats[myStream].upBytes += currUp - prevUp;
streamStats[myStream].downBytes += currDown - prevDown;
}
}
}
}
@ -1437,29 +1478,9 @@ void Controller::handlePrometheus(HTTP::Parser &H, Socket::Connection &conn, int
H.StartResponse("200", "OK", H, conn, true);
// Collect core server stats
uint64_t cpu_use = 0;
uint64_t mem_total = 0, mem_free = 0, mem_bufcache = 0;
uint64_t bw_up_total = 0, bw_down_total = 0;
{
std::ifstream cpustat("/proc/stat");
if (cpustat){
char line[300];
while (cpustat.getline(line, 300)){
static unsigned long long cl_total = 0, cl_idle = 0;
unsigned long long c_user, c_nice, c_syst, c_idle, c_total;
if (sscanf(line, "cpu %Lu %Lu %Lu %Lu", &c_user, &c_nice, &c_syst, &c_idle) == 4){
c_total = c_user + c_nice + c_syst + c_idle;
if (c_total > cl_total){
cpu_use = (long long int)(1000 - ((c_idle - cl_idle) * 1000) / (c_total - cl_total));
}else{
cpu_use = 0;
}
cl_total = c_total;
cl_idle = c_idle;
break;
}
}
}
std::ifstream meminfo("/proc/meminfo");
if (meminfo){
char line[300];

View file

@ -1,5 +1,6 @@
#include "controller_capabilities.h"
#include "controller_storage.h"
#include "controller_push.h" //LTS
#include <algorithm>
#include <fstream>
#include <iostream>
@ -41,7 +42,7 @@ namespace Controller{
///\brief Store and print a log message.
///\param kind The type of message.
///\param message The message to be logged.
void Log(const std::string &kind, const std::string &message, const std::string &stream, bool noWriteToLog){
void Log(const std::string &kind, const std::string &message, const std::string &stream, uint64_t progPid, bool noWriteToLog){
if (noWriteToLog){
tthread::lock_guard<tthread::mutex> guard(logMutex);
JSON::Value m;
@ -52,6 +53,7 @@ namespace Controller{
m.append(stream);
Storage["log"].append(m);
Storage["log"].shrink(100); // limit to 100 log messages
if (isPushActive(progPid)){pushLogMessage(progPid, m);} //LTS
logCounter++;
if (rlxLogs && rlxLogs->isReady()){
if (!firstLog){firstLog = logCounter;}

View file

@ -22,7 +22,7 @@ namespace Controller{
Util::RelAccX *streamsAccessor();
/// Store and print a log message.
void Log(const std::string &kind, const std::string &message, const std::string &stream = "",
void Log(const std::string &kind, const std::string &message, const std::string &stream = "", uint64_t progPid = 0,
bool noWriteToLog = false);
void logAccess(const std::string &sessId, const std::string &strm, const std::string &conn,
const std::string &host, uint64_t duration, uint64_t up, uint64_t down,