Updated triggers, implemented LIVE_BANDWIDTH trigger
This commit is contained in:
parent
6a68d86a0e
commit
cab87a6425
6 changed files with 319 additions and 324 deletions
|
@ -95,7 +95,7 @@ static const char * DBG_LVL_LIST[] = {"NONE", "FAIL", "ERROR", "WARN", "INFO", "
|
||||||
#define SHM_TRACK_DATA "MstDATA%s@%lu_%lu" //%s stream name, %lu track ID, %lu page #
|
#define SHM_TRACK_DATA "MstDATA%s@%lu_%lu" //%s stream name, %lu track ID, %lu page #
|
||||||
#define SHM_STATISTICS "MstSTAT"
|
#define SHM_STATISTICS "MstSTAT"
|
||||||
#define SHM_USERS "MstUSER%s" //%s stream name
|
#define SHM_USERS "MstUSER%s" //%s stream name
|
||||||
#define SHM_TRIGGER "MstTRIG%s" //%s trigger name
|
#define SHM_TRIGGER "MstTRGR%s" //%s trigger name
|
||||||
#define SEM_LIVE "/MstLIVE%s" //%s stream name
|
#define SEM_LIVE "/MstLIVE%s" //%s stream name
|
||||||
#define SEM_INPUT "/MstInpt%s" //%s stream name
|
#define SEM_INPUT "/MstInpt%s" //%s stream name
|
||||||
#define SEM_CONF "/MstConfLock"
|
#define SEM_CONF "/MstConfLock"
|
||||||
|
|
150
lib/triggers.cpp
150
lib/triggers.cpp
|
@ -5,19 +5,22 @@
|
||||||
/// Triggers are the preferred way of responding to server events. Each trigger has a name and a payload, and may be stream-specific or global.
|
/// Triggers are the preferred way of responding to server events. Each trigger has a name and a payload, and may be stream-specific or global.
|
||||||
///
|
///
|
||||||
/// Triggers may be handled by a URL or an executable. If the handler contains ://, a HTTP URL is assumed. Otherwise, an executable is assumed.
|
/// Triggers may be handled by a URL or an executable. If the handler contains ://, a HTTP URL is assumed. Otherwise, an executable is assumed.
|
||||||
/// If handled by an URL, a POST request is sent to the URL with an extra X-Trigger header containing the trigger name and the payload as the POST body.
|
/// If handled by an URL, a POST request is sent to the URL with an extra X-Trigger header containing the trigger name and the payload as the
|
||||||
/// If handled by an executable, it's started with the trigger name as its only argument, and the payload is piped into the executable over standard input.
|
/// POST body.
|
||||||
|
/// If handled by an executable, it's started with the trigger name as its only argument, and the payload is piped into the executable over
|
||||||
|
/// standard input.
|
||||||
///
|
///
|
||||||
/// Currently, all triggers are handled asynchronously and responses (if any) are completely ignored. In the future this may change.
|
/// Currently, all triggers are handled asynchronously and responses (if any) are completely ignored. In the future this may change.
|
||||||
///
|
///
|
||||||
|
|
||||||
#include <string.h>//for strncmp
|
|
||||||
#include "triggers.h"
|
#include "triggers.h"
|
||||||
#include "http_parser.h"//for sending http request
|
#include "bitfields.h" //for strToBool
|
||||||
#include "defines.h" //for FAIL_MSG and INFO_MSG
|
#include "defines.h" //for FAIL_MSG and INFO_MSG
|
||||||
|
#include "http_parser.h" //for sending http request
|
||||||
#include "procs.h" //for StartPiped
|
#include "procs.h" //for StartPiped
|
||||||
#include "shared_memory.h"
|
#include "shared_memory.h"
|
||||||
#include "bitfields.h" //for strToBool
|
#include "util.h"
|
||||||
|
#include <string.h> //for strncmp
|
||||||
|
|
||||||
namespace Triggers{
|
namespace Triggers{
|
||||||
|
|
||||||
|
@ -82,16 +85,12 @@ std::string handleTrigger(const std::string &trigger, const std::string &value,
|
||||||
close(fdIn);
|
close(fdIn);
|
||||||
|
|
||||||
if (sync){// if sync!=0 wait for response
|
if (sync){// if sync!=0 wait for response
|
||||||
while (Util::Procs::isActive(myProc)) {
|
while (Util::Procs::isActive(myProc)){Util::sleep(100);}
|
||||||
Util::sleep(100);
|
|
||||||
}
|
|
||||||
std::string ret;
|
std::string ret;
|
||||||
FILE *outFile = fdopen(fdOut, "r");
|
FILE *outFile = fdopen(fdOut, "r");
|
||||||
char *fileBuf = 0;
|
char *fileBuf = 0;
|
||||||
size_t fileBufLen = 0;
|
size_t fileBufLen = 0;
|
||||||
while (!(feof(outFile) || ferror(outFile)) && (getline(&fileBuf, &fileBufLen, outFile) != -1)) {
|
while (!(feof(outFile) || ferror(outFile)) && (getline(&fileBuf, &fileBufLen, outFile) != -1)){ret += fileBuf;}
|
||||||
ret += fileBuf;
|
|
||||||
}
|
|
||||||
fclose(outFile);
|
fclose(outFile);
|
||||||
free(fileBuf);
|
free(fileBuf);
|
||||||
close(fdOut);
|
close(fdOut);
|
||||||
|
@ -104,45 +103,18 @@ std::string handleTrigger(const std::string &trigger, const std::string &value,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string empty;
|
static std::string usually_empty;
|
||||||
|
|
||||||
///\brief Checks if one or more triggers are defined that should be handled for all streams (for a trigger event type)
|
|
||||||
///\param type Trigger event type.
|
|
||||||
///\return returns true, if so
|
|
||||||
///calls doTrigger with dryRun set to true
|
|
||||||
bool shouldTrigger(const std::string type){ //returns true if a trigger of the specified type should be handled for all streams
|
|
||||||
static std::string empty;
|
|
||||||
empty.clear();
|
|
||||||
return doTrigger(type, empty, empty, true, empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
///\brief returns true if a trigger of the specified type should be handled for a specified stream (, or entire server)
|
///\brief returns true if a trigger of the specified type should be handled for a specified stream (, or entire server)
|
||||||
///\param type Trigger event type.
|
///\param type Trigger event type.
|
||||||
///\param streamName the stream to be handled
|
///\param streamName the stream to be handled
|
||||||
///\return returns true if so
|
///\return returns true if so
|
||||||
/// calls doTrigger with dryRun set to true
|
/// calls doTrigger with dryRun set to true
|
||||||
bool shouldTrigger(const std::string type, const std::string &streamName){ //returns true if a trigger of the specified type should be handled for a specified stream (, or entire server)
|
/// returns true if a trigger of the specified type should be
|
||||||
empty.clear();
|
/// handled for a specified stream (, or entire server)
|
||||||
return doTrigger(type, empty, streamName, true, empty);
|
bool shouldTrigger(const std::string & type, const std::string & streamName, bool paramsCB(const char *, const void *), const void * extraParam){
|
||||||
}
|
usually_empty.clear();
|
||||||
|
return doTrigger(type, empty, streamName, true, usually_empty, paramsCB, extraParam);
|
||||||
///\brief handles triggers for a specific trigger event type, without a payload, server-wide
|
|
||||||
///\param type Trigger event type.
|
|
||||||
///\returns Boolean, false if further processing should be aborted.
|
|
||||||
///calls doTrigger with dryRun set to false
|
|
||||||
bool doTrigger(const std::string type){
|
|
||||||
empty.clear();
|
|
||||||
return doTrigger(type, empty, empty, false, empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
///\brief handles triggers for a specific trigger event type, with a payload, server-wide
|
|
||||||
///\param type Trigger event type.
|
|
||||||
///\param payload Trigger type-specific data
|
|
||||||
///\returns Boolean, false if further processing should be aborted.
|
|
||||||
///calls doTrigger with dryRun set to false
|
|
||||||
bool doTrigger(const std::string type, const std::string &payload){
|
|
||||||
empty.clear();
|
|
||||||
return doTrigger(type, payload, empty, false, empty);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///\brief handles triggers for a specific trigger event type, with a payload, for a specified stream, and/or server-wide
|
///\brief handles triggers for a specific trigger event type, with a payload, for a specified stream, and/or server-wide
|
||||||
|
@ -151,9 +123,9 @@ bool doTrigger(const std::string type, const std::string &payload){
|
||||||
///\param streamName The name of a stream.
|
///\param streamName The name of a stream.
|
||||||
///\returns Boolean, false if further processing should be aborted.
|
///\returns Boolean, false if further processing should be aborted.
|
||||||
/// calls doTrigger with dryRun set to false
|
/// calls doTrigger with dryRun set to false
|
||||||
bool doTrigger(const std::string type, const std::string &payload, const std::string &streamName){
|
bool doTrigger(const std::string & type, const std::string &payload, const std::string &streamName){
|
||||||
empty.clear();
|
usually_empty.clear();
|
||||||
return doTrigger(type, payload, streamName, false, empty);
|
return doTrigger(type, payload, streamName, false, usually_empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
///\brief
|
///\brief
|
||||||
|
@ -163,73 +135,64 @@ bool doTrigger(const std::string type, const std::string &payload, const std::st
|
||||||
///\param dryRun determines the mode of operation for this function
|
///\param dryRun determines the mode of operation for this function
|
||||||
///\param response Returns the last received response by reference
|
///\param response Returns the last received response by reference
|
||||||
///\returns Boolean, false if further processing should be aborted
|
///\returns Boolean, false if further processing should be aborted
|
||||||
///This function attempts to open and parse a shared memory page with the config for a trigger event type, in order to parse the triggers defined for that trigger event type.
|
/// This function attempts to open and parse a shared memory page with the config for a trigger event type, in order to parse the triggers
|
||||||
|
/// defined for that trigger event type.
|
||||||
/// The function can be used for two separate purposes, determined by the value of dryRun
|
/// The function can be used for two separate purposes, determined by the value of dryRun
|
||||||
///-if this function is called with dryRun==true (for example, from a handleTrigger function), the return value will be true, if at least one trigger should be handled for the requested type/stream.
|
///-if this function is called with dryRun==true (for example, from a handleTrigger function), the return value will be true, if at least one
|
||||||
|
///trigger should be handled for the requested type/stream.
|
||||||
/// this can be used to make sure a payload is only generated if at least one trigger should be handled.
|
/// this can be used to make sure a payload is only generated if at least one trigger should be handled.
|
||||||
///-if this function is called with dryRun==false (for example, from one of the overloaded doTrigger functions), handleTrigger is called for all configured triggers. In that case, the return value does not matter, it will probably be false in all cases.
|
///-if this function is called with dryRun==false (for example, from one of the overloaded doTrigger functions), handleTrigger is called for
|
||||||
bool doTrigger(const std::string type, const std::string &payload, const std::string &streamName, bool dryRun, std::string & response){
|
///all configured triggers. In that case, the return value does not matter, it will probably be false in all cases.
|
||||||
|
bool doTrigger(const std::string & type, const std::string &payload, const std::string &streamName, bool dryRun, std::string &response, bool paramsCB(const char *, const void *), const void * extraParam){
|
||||||
// open SHM page for this type:
|
// open SHM page for this type:
|
||||||
char thisPageName[NAME_BUFFER_SIZE];
|
char thisPageName[NAME_BUFFER_SIZE];
|
||||||
snprintf(thisPageName, NAME_BUFFER_SIZE, SHM_TRIGGER, type.c_str());
|
snprintf(thisPageName, NAME_BUFFER_SIZE, SHM_TRIGGER, type.c_str());
|
||||||
IPC::sharedPage typePage(thisPageName, 8 * 1024, false, false);
|
IPC::sharedPage typePage(thisPageName, 8 * 1024, false, false);
|
||||||
if (!typePage.mapped){// page doesn't exist?
|
if (!typePage.mapped){// page doesn't exist?
|
||||||
HIGH_MSG("No triggers for %s defined: list does not exist", type.c_str());
|
VERYHIGH_MSG("No triggers for %s found", type.c_str());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
char* bytepos = typePage.mapped; //not checking page size. will probably be fine.
|
Util::RelAccX trigs(typePage.mapped, false);
|
||||||
char* startBytepos=bytepos;
|
if (!trigs.isReady()){
|
||||||
unsigned int totalLen = ((unsigned int *)bytepos)[0];
|
WARN_MSG("No triggers for %s: list not ready", type.c_str());
|
||||||
bool retVal = true;
|
return false;
|
||||||
VERYHIGH_MSG("Parsing %lu bytes of triggers for %s, stream: %s", totalLen, type.c_str(), streamName.c_str());
|
}
|
||||||
std::string uri;
|
|
||||||
unsigned int sync=0;
|
|
||||||
|
|
||||||
while( totalLen != 0 && bytepos < typePage.mapped + typePage.len ){
|
|
||||||
unsigned int uriLen = ((unsigned int *)bytepos)[1];
|
|
||||||
bytepos+=4+4;
|
|
||||||
uri=std::string(bytepos,uriLen);
|
|
||||||
bytepos+=uriLen;
|
|
||||||
sync=bytepos[0];
|
|
||||||
bytepos++;
|
|
||||||
|
|
||||||
bool isHandled = false;
|
|
||||||
if(totalLen>((unsigned int)(bytepos-startBytepos))){
|
|
||||||
while( totalLen>((unsigned int)(bytepos-startBytepos)) ){
|
|
||||||
unsigned int stringLen=((unsigned int *)bytepos)[0];
|
|
||||||
bytepos+=4;
|
|
||||||
size_t splitter = streamName.find_first_of("+ ");
|
size_t splitter = streamName.find_first_of("+ ");
|
||||||
if ((streamName.size() == stringLen || splitter == stringLen) && strncmp(bytepos, streamName.c_str(), stringLen) == 0){
|
bool retVal = true;
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < trigs.getRCount(); ++i){
|
||||||
|
std::string uri = std::string(trigs.getPointer("url", i));
|
||||||
|
uint8_t sync = trigs.getInt("sync", i);
|
||||||
|
|
||||||
|
char * strPtr = trigs.getPointer("streams", i);
|
||||||
|
uint32_t pLen = trigs.getSize("streams");
|
||||||
|
uint32_t bPos = 0;
|
||||||
|
|
||||||
|
bool isHandled = !streamName.size();
|
||||||
|
while (bPos + 4 < pLen){
|
||||||
|
uint32_t stringLen = ((unsigned int *)(strPtr+bPos))[0];
|
||||||
|
if (bPos + 4 + stringLen > pLen || !stringLen){break;}
|
||||||
|
if ((streamName.size() == stringLen || splitter == stringLen) && strncmp(strPtr+bPos+4, streamName.data(), stringLen) == 0){
|
||||||
isHandled = true;
|
isHandled = true;
|
||||||
}
|
}
|
||||||
bytepos+=stringLen;
|
bPos += stringLen + 4;
|
||||||
}
|
}
|
||||||
if (!streamName.size()){
|
|
||||||
isHandled = true;
|
|
||||||
}
|
|
||||||
} else if(totalLen==((unsigned int)(bytepos-startBytepos))){
|
|
||||||
// no streams explicitly defined for this trigger, return true for all streams.
|
// no streams explicitly defined for this trigger, return true for all streams.
|
||||||
|
if (bPos <= 4){
|
||||||
isHandled = true;
|
isHandled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isHandled && paramsCB){
|
||||||
|
isHandled = paramsCB(trigs.getPointer("params", i), extraParam);
|
||||||
|
}
|
||||||
|
|
||||||
if (isHandled){
|
if (isHandled){
|
||||||
VERYHIGH_MSG("%s trigger handled by %s", type.c_str(), uri.c_str());
|
INFO_MSG("%s trigger handled by %s", type.c_str(), uri.c_str());
|
||||||
if(dryRun){
|
if (dryRun){return true;}
|
||||||
return true;
|
|
||||||
}
|
|
||||||
response = handleTrigger(type, uri, payload, sync); // do it.
|
response = handleTrigger(type, uri, payload, sync); // do it.
|
||||||
retVal &= Util::stringToBool(response);
|
retVal &= Util::stringToBool(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(totalLen!=((unsigned int)(bytepos-startBytepos))){ //if this is not the case, something bad might have happened.
|
|
||||||
ERROR_MSG("Error in %s trigger, totalLen: %d current position from startBytepos: %d", type.c_str(),totalLen, (unsigned int)(bytepos-startBytepos));
|
|
||||||
break; //stop reading hypothetical garbage
|
|
||||||
}
|
|
||||||
|
|
||||||
startBytepos=startBytepos+totalLen; //init next iteration
|
|
||||||
bytepos=startBytepos;
|
|
||||||
totalLen = ((unsigned int *)bytepos)[0]; //read next size
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dryRun){
|
if (dryRun){
|
||||||
|
@ -238,6 +201,5 @@ bool doTrigger(const std::string type, const std::string &payload, const std::st
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} //end namespace Controller
|
|
||||||
|
|
||||||
|
|
|
@ -2,13 +2,14 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace Triggers{
|
namespace Triggers{
|
||||||
bool doTrigger(const std::string triggerType, const std::string &payload, const std::string &streamName, bool dryRun, std::string & response);
|
|
||||||
|
static const std::string empty;
|
||||||
|
|
||||||
|
bool doTrigger(const std::string & triggerType, const std::string &payload, const std::string &streamName, bool dryRun, std::string &response, bool paramsCB(const char *, const void *) = 0, const void * extraParam = 0);
|
||||||
std::string handleTrigger(const std::string &triggerType, const std::string &value, const std::string &payload, int sync);
|
std::string handleTrigger(const std::string &triggerType, const std::string &value, const std::string &payload, int sync);
|
||||||
|
|
||||||
// All of the below are just shorthands for specific usage of the doTrigger function above:
|
// All of the below are just shorthands for specific usage of the doTrigger function above:
|
||||||
bool shouldTrigger(const std::string triggerType);
|
bool shouldTrigger(const std::string & triggerType, const std::string &streamName = empty, bool paramsCB(const char *, const void *) = 0, const void * extraParam = 0);
|
||||||
bool shouldTrigger(const std::string triggerType, const std::string &streamName);
|
bool doTrigger(const std::string & triggerType, const std::string & payload = empty, const std::string & streamName = empty);
|
||||||
bool doTrigger(const std::string triggerType);
|
|
||||||
bool doTrigger(const std::string triggerType, const std::string &payload);
|
|
||||||
bool doTrigger(const std::string triggerType, const std::string &payload, const std::string &streamName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,8 @@ namespace Util {
|
||||||
#define RAX_128STRING 0x33
|
#define RAX_128STRING 0x33
|
||||||
#define RAX_256STRING 0x34
|
#define RAX_256STRING 0x34
|
||||||
#define RAX_RAW 0x40
|
#define RAX_RAW 0x40
|
||||||
|
#define RAX_256RAW 0x44
|
||||||
|
#define RAX_512RAW 0x45
|
||||||
|
|
||||||
/// Reliable Access class.
|
/// Reliable Access class.
|
||||||
/// Provides reliable access to memory data structures, using dynamic static offsets and a status field.
|
/// Provides reliable access to memory data structures, using dynamic static offsets and a status field.
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <iostream>
|
|
||||||
#include <fstream>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <mist/timing.h>
|
|
||||||
#include <mist/shared_memory.h>
|
|
||||||
#include <mist/defines.h>
|
|
||||||
#include "controller_storage.h"
|
#include "controller_storage.h"
|
||||||
#include "controller_capabilities.h"
|
#include "controller_capabilities.h"
|
||||||
|
#include <algorithm>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <mist/defines.h>
|
||||||
|
#include <mist/shared_memory.h>
|
||||||
|
#include <mist/timing.h>
|
||||||
#include <mist/triggers.h> //LTS
|
#include <mist/triggers.h> //LTS
|
||||||
|
#include <mist/util.h> //LTS
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
///\brief Holds everything unique to the controller.
|
///\brief Holds everything unique to the controller.
|
||||||
namespace Controller{
|
namespace Controller{
|
||||||
|
@ -61,13 +62,9 @@ namespace Controller {
|
||||||
FILE *output = fdopen((long long int)err, "r");
|
FILE *output = fdopen((long long int)err, "r");
|
||||||
while (fgets(buf, 1024, output)){
|
while (fgets(buf, 1024, output)){
|
||||||
unsigned int i = 0;
|
unsigned int i = 0;
|
||||||
while (i < 9 && buf[i] != '|' && buf[i] != 0){
|
while (i < 9 && buf[i] != '|' && buf[i] != 0){++i;}
|
||||||
++i;
|
|
||||||
}
|
|
||||||
unsigned int j = i;
|
unsigned int j = i;
|
||||||
while (j < 1024 && buf[j] != '\n' && buf[j] != 0){
|
while (j < 1024 && buf[j] != '\n' && buf[j] != 0){++j;}
|
||||||
++j;
|
|
||||||
}
|
|
||||||
buf[j] = 0;
|
buf[j] = 0;
|
||||||
if (i < 9){
|
if (i < 9){
|
||||||
buf[i] = 0;
|
buf[i] = 0;
|
||||||
|
@ -83,9 +80,11 @@ namespace Controller {
|
||||||
|
|
||||||
/// Writes the current config to shared memory to be used in other processes
|
/// Writes the current config to shared memory to be used in other processes
|
||||||
/// \triggers
|
/// \triggers
|
||||||
/// The `"SYSTEM_START"` trigger is global, and is ran as soon as the server configuration is first stable. It has no payload. If cancelled, the system immediately shuts down again.
|
/// The `"SYSTEM_START"` trigger is global, and is ran as soon as the server configuration is first stable. It has no payload. If cancelled,
|
||||||
|
/// the system immediately shuts down again.
|
||||||
/// \n
|
/// \n
|
||||||
/// The `"SYSTEM_CONFIG"` trigger is global, and is ran every time the server configuration is updated. Its payload is the new configuration in JSON format. This trigger cannot be cancelled.
|
/// The `"SYSTEM_CONFIG"` trigger is global, and is ran every time the server configuration is updated. Its payload is the new configuration in
|
||||||
|
/// JSON format. This trigger cannot be cancelled.
|
||||||
void writeConfig(){
|
void writeConfig(){
|
||||||
static JSON::Value writeConf;
|
static JSON::Value writeConf;
|
||||||
bool changed = false;
|
bool changed = false;
|
||||||
|
@ -127,55 +126,77 @@ namespace Controller {
|
||||||
// for all shm pages that hold triggers
|
// for all shm pages that hold triggers
|
||||||
pageForType.clear();
|
pageForType.clear();
|
||||||
|
|
||||||
if( writeConf["config"]["triggers"].size() ){//if triggers are defined...
|
if (writeConf["config"]["triggers"].size()){
|
||||||
jsonForEach(writeConf["config"]["triggers"], it){//for all types defined in config
|
jsonForEach(writeConf["config"]["triggers"], it){
|
||||||
snprintf(tmpBuf,NAME_BUFFER_SIZE,SHM_TRIGGER,(it.key()).c_str()); //create page
|
snprintf(tmpBuf, NAME_BUFFER_SIZE, SHM_TRIGGER, (it.key()).c_str());
|
||||||
pageForType[it.key()].init(tmpBuf, 8*1024, true, false);// todo: should this be false/why??
|
pageForType[it.key()].init(tmpBuf, 32 * 1024, true, false);
|
||||||
char * bytePos=pageForType[it.key()].mapped;
|
Util::RelAccX tPage(pageForType[it.key()].mapped, false);
|
||||||
|
tPage.addField("url", RAX_128STRING);
|
||||||
|
tPage.addField("sync", RAX_UINT);
|
||||||
|
tPage.addField("streams", RAX_256RAW);
|
||||||
|
tPage.addField("params", RAX_128STRING);
|
||||||
|
tPage.setReady();
|
||||||
|
uint32_t i = 0;
|
||||||
|
uint32_t max = (32 * 1024 - tPage.getOffset()) / tPage.getRSize();
|
||||||
|
|
||||||
// write data to page
|
// write data to page
|
||||||
jsonForEach(*it, triggIt){ //for all defined
|
jsonForEach(*it, triggIt){
|
||||||
unsigned int tmpUrlSize=(*triggIt)[(unsigned int) 0].asStringRef().size();
|
if (i >= max){
|
||||||
unsigned int tmpStreamNames=0;// (*triggIt)[2ul].packedSize();
|
ERROR_MSG("Not all %s triggers fit on the memory page!", (it.key()).c_str());
|
||||||
std::string namesArray="";
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if( (triggIt->size() >= 3) && (*triggIt)[2ul].size()){
|
if (triggIt->isArray()){
|
||||||
jsonForEach((*triggIt)[2ul], shIt){
|
tPage.setString("url", (*triggIt)[0u].asStringRef(), i);
|
||||||
unsigned int tmpLen=shIt->asString().size();
|
tPage.setInt("sync", ((*triggIt)[1u].asBool() ? 1 : 0), i);
|
||||||
tmpStreamNames+= 4+tmpLen;
|
char *strmP = tPage.getPointer("streams", i);
|
||||||
//INFO_MSG("adding string: %s len: %d", shIt->asString().c_str() , tmpLen );
|
if (strmP){
|
||||||
((unsigned int*)tmpBuf)[0] = tmpLen; //NOTE: namesArray may be replaced by writing directly to tmpBuf.
|
((unsigned int *)strmP)[0] = 0; // reset first 4 bytes of stream list pointer
|
||||||
|
if ((triggIt->size() >= 3) && (*triggIt)[2u].size()){
|
||||||
|
std::string namesArray;
|
||||||
|
jsonForEach((*triggIt)[2u], shIt){
|
||||||
|
((unsigned int *)tmpBuf)[0] = shIt->asString().size();
|
||||||
namesArray.append(tmpBuf, 4);
|
namesArray.append(tmpBuf, 4);
|
||||||
namesArray.append(shIt->asString());
|
namesArray.append(shIt->asString());
|
||||||
}
|
}
|
||||||
|
if (namesArray.size()){memcpy(strmP, namesArray.data(), std::min(namesArray.size(), (size_t)256));}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
unsigned int totalLen=9+tmpUrlSize+tmpStreamNames; //4Btotal len, 4Burl len ,XB tmpurl, 1B sync , XB tmpstreamnames
|
|
||||||
|
|
||||||
if(totalLen > (pageForType[it.key()].len-(bytePos-pageForType[it.key()].mapped)) ){ //check if totalLen fits on the page
|
|
||||||
ERROR_MSG("trigger does not fit on page. size: %d bytes left on page: %d skipping.",totalLen,(pageForType[it.key()].len-(bytePos-pageForType[it.key()].mapped))); //doesnt fit
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
((unsigned int*)bytePos)[0] = totalLen;
|
if (triggIt->isObject()){
|
||||||
bytePos+=4;
|
if (!triggIt->isMember("handler")){continue;}
|
||||||
((unsigned int*)bytePos)[0] = tmpUrlSize;
|
tPage.setString("url", (*triggIt)["handler"].asStringRef(), i);
|
||||||
bytePos+=4;
|
tPage.setInt("sync", ((*triggIt)["sync"].asBool() ? 1 : 0), i);
|
||||||
memcpy(bytePos, (*triggIt)[(unsigned int) 0].asStringRef().data(), (*triggIt)[(unsigned int) 0].asStringRef().size());
|
char *strmP = tPage.getPointer("streams", i);
|
||||||
bytePos+=(*triggIt)[(unsigned int) 0].asStringRef().size();
|
if (strmP){
|
||||||
(bytePos++)[0] = (*triggIt)[1ul].asBool() ? '\001' : '\000';
|
((unsigned int *)strmP)[0] = 0; // reset first 4 bytes of stream list pointer
|
||||||
if(tmpStreamNames){
|
if ((triggIt->isMember("streams")) && (*triggIt)["streams"].size()){
|
||||||
memcpy(bytePos,namesArray.data(),tmpStreamNames); //contains a string of 4Blen,XBstring pairs
|
std::string namesArray;
|
||||||
bytePos+=tmpStreamNames;
|
jsonForEach((*triggIt)["streams"], shIt){
|
||||||
|
((unsigned int *)tmpBuf)[0] = shIt->asString().size();
|
||||||
|
namesArray.append(tmpBuf, 4);
|
||||||
|
namesArray.append(shIt->asString());
|
||||||
|
}
|
||||||
|
if (namesArray.size()){memcpy(strmP, namesArray.data(), std::min(namesArray.size(), (size_t)256));}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (triggIt->isMember("params")){
|
||||||
|
tPage.setString("params", (*triggIt)["params"].asStringRef(), i);
|
||||||
|
}else{
|
||||||
|
tPage.setString("params", "", i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
tPage.setRCount(std::min(i, max));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool serverStartTriggered;
|
static bool serverStartTriggered;
|
||||||
if (!serverStartTriggered){
|
if (!serverStartTriggered){
|
||||||
if (!Triggers::doTrigger("SYSTEM_START")){
|
if (!Triggers::doTrigger("SYSTEM_START")){conf.is_active = false;}
|
||||||
conf.is_active = false;
|
|
||||||
}
|
|
||||||
serverStartTriggered++;
|
serverStartTriggered++;
|
||||||
}
|
}
|
||||||
if (Triggers::shouldTrigger("SYSTEM_CONFIG")){
|
if (Triggers::shouldTrigger("SYSTEM_CONFIG")){
|
||||||
|
@ -183,19 +204,6 @@ namespace Controller {
|
||||||
Triggers::doTrigger("SYSTEM_CONFIG", payload);
|
Triggers::doTrigger("SYSTEM_CONFIG", payload);
|
||||||
}
|
}
|
||||||
/*LTS-END*/
|
/*LTS-END*/
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*NOTES:
|
|
||||||
4B size (total size of entry 9B+XB(URL)+ 0..XB(nameArrayLen)) (if 0x00, stop reading)
|
|
||||||
4B url_len
|
|
||||||
XB url
|
|
||||||
1B async
|
|
||||||
for(number of strings)
|
|
||||||
4B stringLen
|
|
||||||
XB string
|
|
||||||
)
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
|
@ -253,6 +253,14 @@ namespace Mist {
|
||||||
//return is by reference
|
//return is by reference
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*LTS-START*/
|
||||||
|
static bool liveBW(const char * param, const void * bwPtr){
|
||||||
|
if (!param || !bwPtr){return false;}
|
||||||
|
INFO_MSG("Comparing %s to %lu", param, *((uint32_t*)bwPtr));
|
||||||
|
return JSON::Value(param).asInt() <= *((uint32_t*)bwPtr);
|
||||||
|
}
|
||||||
|
/*LTS-END*/
|
||||||
|
|
||||||
/// \triggers
|
/// \triggers
|
||||||
/// The `"STREAM_BUFFER"` trigger is stream-specific, and is ran whenever the buffer changes state between playable (FULL) or not (EMPTY). It cannot be cancelled. It is possible to receive multiple EMPTY calls without FULL calls in between, as EMPTY is always generated when a stream is unloaded from memory, even if this stream never reached playable state in the first place (e.g. a broadcast was cancelled before filling enough buffer to be playable). Its payload is:
|
/// The `"STREAM_BUFFER"` trigger is stream-specific, and is ran whenever the buffer changes state between playable (FULL) or not (EMPTY). It cannot be cancelled. It is possible to receive multiple EMPTY calls without FULL calls in between, as EMPTY is always generated when a stream is unloaded from memory, even if this stream never reached playable state in the first place (e.g. a broadcast was cancelled before filling enough buffer to be playable). Its payload is:
|
||||||
/// ~~~~~~~~~~~~~~~
|
/// ~~~~~~~~~~~~~~~
|
||||||
|
@ -263,10 +271,13 @@ namespace Mist {
|
||||||
void inputBuffer::updateMeta() {
|
void inputBuffer::updateMeta() {
|
||||||
static bool wentDry = false;
|
static bool wentDry = false;
|
||||||
static long long unsigned int lastFragCount = 0xFFFFull;
|
static long long unsigned int lastFragCount = 0xFFFFull;
|
||||||
|
static uint32_t lastBPS = 0;/*LTS*/
|
||||||
|
uint32_t currBPS = 0;
|
||||||
long long unsigned int firstms = 0xFFFFFFFFFFFFFFFFull;
|
long long unsigned int firstms = 0xFFFFFFFFFFFFFFFFull;
|
||||||
long long unsigned int lastms = 0;
|
long long unsigned int lastms = 0;
|
||||||
long long unsigned int fragCount = 0xFFFFull;
|
long long unsigned int fragCount = 0xFFFFull;
|
||||||
for (std::map<unsigned int, DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++) {
|
for (std::map<unsigned int, DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++) {
|
||||||
|
currBPS += it->second.bps; /*LTS*/
|
||||||
if (it->second.type == "meta" || !it->second.type.size()) {
|
if (it->second.type == "meta" || !it->second.type.size()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -290,20 +301,31 @@ namespace Mist {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*LTS-START*/
|
/*LTS-START*/
|
||||||
if (fragCount >= FRAG_BOOT && fragCount != 0xFFFFull && Triggers::shouldTrigger("STREAM_BUFFER")){
|
if (currBPS != lastBPS){
|
||||||
|
lastBPS = currBPS;
|
||||||
|
if (Triggers::shouldTrigger("LIVE_BANDWIDTH", streamName, liveBW, &lastBPS)){
|
||||||
|
std::string payload = streamName + "\n" + JSON::Value((long long)lastBPS).asStringRef();
|
||||||
|
if (!Triggers::doTrigger("LIVE_BANDWIDTH", payload, streamName)){
|
||||||
|
WARN_MSG("Shutting down buffer because bandwidth limit reached!");
|
||||||
|
config->is_active = false;
|
||||||
|
userPage.finishEach();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (fragCount >= FRAG_BOOT && fragCount != 0xFFFFull && Triggers::shouldTrigger("STREAM_BUFFER", streamName)){
|
||||||
JSON::Value stream_details;
|
JSON::Value stream_details;
|
||||||
fillBufferDetails(stream_details);
|
fillBufferDetails(stream_details);
|
||||||
if (lastFragCount == 0xFFFFull) {
|
if (lastFragCount == 0xFFFFull) {
|
||||||
std::string payload = config->getString("streamname") + "\nFULL\n" + stream_details.toString();
|
std::string payload = streamName + "\nFULL\n" + stream_details.toString();
|
||||||
Triggers::doTrigger("STREAM_BUFFER", payload, config->getString("streamname"));
|
Triggers::doTrigger("STREAM_BUFFER", payload, streamName);
|
||||||
}else{
|
}else{
|
||||||
if (stream_details.isMember("issues") != wentDry){
|
if (stream_details.isMember("issues") != wentDry){
|
||||||
if (stream_details.isMember("issues")){
|
if (stream_details.isMember("issues")){
|
||||||
std::string payload = config->getString("streamname") + "\nDRY\n" + stream_details.toString();
|
std::string payload = streamName + "\nDRY\n" + stream_details.toString();
|
||||||
Triggers::doTrigger("STREAM_BUFFER", payload, config->getString("streamname"));
|
Triggers::doTrigger("STREAM_BUFFER", payload, streamName);
|
||||||
}else{
|
}else{
|
||||||
std::string payload = config->getString("streamname") + "\nRECOVER\n" + stream_details.toString();
|
std::string payload = streamName + "\nRECOVER\n" + stream_details.toString();
|
||||||
Triggers::doTrigger("STREAM_BUFFER", payload, config->getString("streamname"));
|
Triggers::doTrigger("STREAM_BUFFER", payload, streamName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue