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:
parent
2b99f2f5ea
commit
0af992d405
75 changed files with 1512 additions and 790 deletions
|
@ -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
|
||||
|
|
|
@ -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()){
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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;}
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue