Added global configuration mechanism and defaultStream support
This commit is contained in:
parent
50d1d0e944
commit
7bffdfe644
9 changed files with 130 additions and 3 deletions
|
@ -133,6 +133,7 @@ static inline void show_stackframe(){}
|
|||
#define SHM_STREAM_INDEX "MstSTRM%s" //%s stream name
|
||||
#define SHM_STREAM_STATE "MstSTATE%s" //%s stream name
|
||||
#define SHM_STREAM_CONF "MstSCnf%s" //%s stream name
|
||||
#define SHM_GLOBAL_CONF "MstGlobalConfig"
|
||||
#define STRMSTAT_OFF 0
|
||||
#define STRMSTAT_INIT 1
|
||||
#define STRMSTAT_BOOT 2
|
||||
|
|
|
@ -205,12 +205,44 @@ JSON::Value Util::getStreamConfig(const std::string &streamname){
|
|||
Util::DTSCShmReader rStrmConf(tmpBuf);
|
||||
DTSC::Scan stream_cfg = rStrmConf.getScan();
|
||||
if (!stream_cfg){
|
||||
WARN_MSG("Could not get stream '%s' config!", smp.c_str());
|
||||
if (!Util::getGlobalConfig("defaultStream")){
|
||||
WARN_MSG("Could not get stream '%s' config!", smp.c_str());
|
||||
}else{
|
||||
INFO_MSG("Could not get stream '%s' config, not emitting WARN message because fallback is configured", smp.c_str());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return stream_cfg.asJSON();
|
||||
}
|
||||
|
||||
JSON::Value Util::getGlobalConfig(const std::string &optionName){
|
||||
IPC::sharedPage globCfg(SHM_GLOBAL_CONF);
|
||||
if (!globCfg.mapped){
|
||||
FAIL_MSG("Could not open global configuration options to read setting for '%s'", optionName.c_str());
|
||||
return JSON::Value();
|
||||
}
|
||||
Util::RelAccX cfgData(globCfg.mapped);
|
||||
if (!cfgData.isReady()){
|
||||
FAIL_MSG("Global configuration options not ready; cannot read setting for '%s'", optionName.c_str());
|
||||
return JSON::Value();
|
||||
}
|
||||
Util::RelAccXFieldData dataField = cfgData.getFieldData(optionName);
|
||||
switch (dataField.type & 0xF0){
|
||||
case RAX_INT:
|
||||
case RAX_UINT:
|
||||
//Integer types, return JSON::Value integer
|
||||
return JSON::Value(cfgData.getInt(dataField));
|
||||
case RAX_RAW:
|
||||
case RAX_STRING:
|
||||
//String types, return JSON::Value string
|
||||
return JSON::Value(std::string(cfgData.getPointer(dataField), cfgData.getSize(optionName)));
|
||||
default:
|
||||
//Unimplemented types
|
||||
FAIL_MSG("Global configuration setting for '%s' is not an implemented datatype!", optionName.c_str());
|
||||
return JSON::Value();
|
||||
}
|
||||
}
|
||||
|
||||
DTSC::Meta Util::getStreamMeta(const std::string &streamname){
|
||||
DTSC::Meta ret;
|
||||
char pageId[NAME_BUFFER_SIZE];
|
||||
|
|
|
@ -17,6 +17,7 @@ namespace Util {
|
|||
bool startInput(std::string streamname, std::string filename = "", bool forkFirst = true, bool isProvider = false, const std::map<std::string, std::string> & overrides = std::map<std::string, std::string>(), pid_t * spawn_pid = NULL);
|
||||
int startPush(const std::string & streamname, std::string & target);
|
||||
JSON::Value getStreamConfig(const std::string & streamname);
|
||||
JSON::Value getGlobalConfig(const std::string & optionName);
|
||||
JSON::Value getInputBySource(const std::string & filename, bool isProvider = false);
|
||||
DTSC::Meta getStreamMeta(const std::string & streamname);
|
||||
uint8_t getStreamStatus(const std::string & streamname);
|
||||
|
|
|
@ -494,6 +494,9 @@ void Controller::handleAPICommands(JSON::Value & Request, JSON::Value & Response
|
|||
out["prometheus"] = in["prometheus"];
|
||||
Controller::prometheus = out["prometheus"].asStringRef();
|
||||
}
|
||||
if (in.isMember("defaultStream")){
|
||||
out["defaultStream"] = in["defaultStream"];
|
||||
}
|
||||
}
|
||||
if (Request.isMember("bandwidth")){
|
||||
if (Request["bandwidth"].isObject()){
|
||||
|
|
|
@ -160,6 +160,13 @@ namespace Controller {
|
|||
trgs["LIVE_BANDWIDTH"]["response"] = "always";
|
||||
trgs["LIVE_BANDWIDTH"]["response_action"] = "If false, shuts down the stream buffer.";
|
||||
trgs["LIVE_BANDWIDTH"]["argument"] = "Triggers only if current bytes per second exceeds this amount (integer)";
|
||||
|
||||
trgs["DEFAULT_STREAM"]["when"] = "When any user attempts to open a stream that cannot be opened (because it is either offline or not configured), allows rewriting the stream to a different one as fallback. Supports variable substitution.";
|
||||
trgs["DEFAULT_STREAM"]["stream_specific"] = true;
|
||||
trgs["DEFAULT_STREAM"]["payload"] = "current defaultStream setting (string)\nrequested stream name (string)\nviewer host (string)\noutput type (string)\nfull request URL (string, may be blank for non-URL-based requests!)";
|
||||
trgs["DEFAULT_STREAM"]["response"] = "always";
|
||||
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.";
|
||||
|
||||
}
|
||||
|
||||
///Aquire list of available protocols, storing in global 'capabilities' JSON::Value.
|
||||
|
|
|
@ -334,6 +334,27 @@ namespace Controller{
|
|||
writeStream(it.key(), *it);
|
||||
}
|
||||
|
||||
{
|
||||
//Global configuration options, if any
|
||||
IPC::sharedPage globCfg;
|
||||
globCfg.init(SHM_GLOBAL_CONF, 4096, false, false);
|
||||
if (!globCfg.mapped){
|
||||
globCfg.init(SHM_GLOBAL_CONF, 4096, true, false);
|
||||
}
|
||||
if (globCfg.mapped){
|
||||
Util::RelAccX globAccX(globCfg.mapped, false);
|
||||
if (!globAccX.isReady()){
|
||||
globAccX.addField("defaultStream", RAX_128STRING);
|
||||
globAccX.setRCount(1);
|
||||
globAccX.setEndPos(1);
|
||||
globAccX.setReady();
|
||||
}
|
||||
globAccX.setString("defaultStream", Storage["config"]["defaultStream"].asStringRef());
|
||||
globCfg.master = false;//leave the page after closing
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*LTS-START*/
|
||||
static std::map<std::string, IPC::sharedPage> pageForType; // should contain one page for every trigger type
|
||||
static JSON::Value writtenTrigs;
|
||||
|
|
|
@ -372,8 +372,31 @@ namespace Mist{
|
|||
}
|
||||
}else{
|
||||
if (!Util::startInput(streamName, "", true, isPushing())){
|
||||
onFail("Stream open failed", true);
|
||||
return;
|
||||
JSON::Value defStrmJson = Util::getGlobalConfig("defaultStream");
|
||||
std::string defStrm = defStrmJson.asString();
|
||||
if(Triggers::shouldTrigger("DEFAULT_STREAM", streamName)){
|
||||
std::string payload = defStrm+"\n"+streamName+"\n" + getConnectedHost() +"\n"+capa["name"].asStringRef()+"\n"+reqUrl;
|
||||
//The return value is ignored, because the response (defStrm in this case) tells us what to do next, if anything.
|
||||
Triggers::doTrigger("DEFAULT_STREAM", payload, streamName, false, defStrm);
|
||||
}
|
||||
if (!defStrm.size()){
|
||||
onFail("Stream open failed", true);
|
||||
return;
|
||||
}
|
||||
std::string newStrm = defStrm;
|
||||
Util::streamVariables(newStrm, streamName, "");
|
||||
if (streamName == newStrm){
|
||||
onFail("Stream open failed; nothing to fall back to ("+defStrm+" == "+newStrm+")", true);
|
||||
return;
|
||||
}
|
||||
INFO_MSG("Stream open failed; falling back to default stream '%s' -> '%s'", defStrm.c_str(), newStrm.c_str());
|
||||
std::string origStream = streamName;
|
||||
streamName = newStrm;
|
||||
Util::Config::streamName = streamName;
|
||||
if (!Util::startInput(streamName, "", true, isPushing())){
|
||||
onFail("Stream open failed (fallback stream for '"+origStream+"')", true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
disconnect();
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "flashPlayer.h"
|
||||
#include "oldFlashPlayer.h"
|
||||
#include <mist/websocket.h>
|
||||
#include <mist/triggers.h>
|
||||
|
||||
namespace Mist {
|
||||
/// Helper function to find the protocol entry for a given port number
|
||||
|
@ -358,8 +359,36 @@ namespace Mist {
|
|||
if (config->getString("nostreamtext") != ""){
|
||||
json_resp["on_error"] = config->getString("nostreamtext");
|
||||
}
|
||||
//Make note of any defaultStream-based redirection
|
||||
if (origStreamName.size() && origStreamName != streamName){
|
||||
json_resp["redirected"].append(origStreamName);
|
||||
json_resp["redirected"].append(streamName);
|
||||
}
|
||||
uint8_t streamStatus = Util::getStreamStatus(streamName);
|
||||
if (streamStatus != STRMSTAT_READY){
|
||||
//If we haven't rewritten the stream name yet to a fallback, attempt to do so
|
||||
if (origStreamName == streamName){
|
||||
JSON::Value defStrmJson = Util::getGlobalConfig("defaultStream");
|
||||
std::string defStrm = defStrmJson.asString();
|
||||
if(Triggers::shouldTrigger("DEFAULT_STREAM", streamName)){
|
||||
std::string payload = defStrm+"\n"+streamName+"\n" + getConnectedHost() +"\n"+capa["name"].asStringRef()+"\n"+reqUrl;
|
||||
//The return value is ignored, because the response (defStrm in this case) tells us what to do next, if anything.
|
||||
Triggers::doTrigger("DEFAULT_STREAM", payload, streamName, false, defStrm);
|
||||
}
|
||||
if (defStrm.size()){
|
||||
std::string newStrm = defStrm;
|
||||
Util::streamVariables(newStrm, streamName, "");
|
||||
if (streamName != newStrm){
|
||||
INFO_MSG("Falling back to default stream '%s' -> '%s'", defStrm.c_str(), newStrm.c_str());
|
||||
origStreamName = streamName;
|
||||
streamName = newStrm;
|
||||
Util::Config::streamName = streamName;
|
||||
reconnect();
|
||||
return getStatusJSON(reqHost, useragent);
|
||||
}
|
||||
}
|
||||
origStreamName.clear();//no fallback, don't check again
|
||||
}
|
||||
switch (streamStatus){
|
||||
case STRMSTAT_OFF:
|
||||
json_resp["error"] = "Stream is offline";
|
||||
|
@ -504,6 +533,7 @@ namespace Mist {
|
|||
}
|
||||
|
||||
void OutHTTP::onHTTP(){
|
||||
origStreamName = streamName;
|
||||
std::string method = H.method;
|
||||
|
||||
//Handle certbot validations
|
||||
|
@ -907,6 +937,8 @@ namespace Mist {
|
|||
Util::startInput(streamName, "", true, false);
|
||||
|
||||
char pageName[NAME_BUFFER_SIZE];
|
||||
std::string currStreamName;
|
||||
currStreamName = streamName;
|
||||
snprintf(pageName, NAME_BUFFER_SIZE, SHM_STREAM_STATE, streamName.c_str());
|
||||
IPC::sharedPage streamStatus(pageName, 1, false, false);
|
||||
uint8_t prevState, newState, metaCounter;
|
||||
|
@ -925,6 +957,11 @@ namespace Mist {
|
|||
disconnect();
|
||||
}
|
||||
JSON::Value resp = getStatusJSON(reqHost, useragent);
|
||||
if (currStreamName != streamName){
|
||||
currStreamName = streamName;
|
||||
snprintf(pageName, NAME_BUFFER_SIZE, SHM_STREAM_STATE, streamName.c_str());
|
||||
streamStatus.close();
|
||||
}
|
||||
ws.sendFrame(resp.toString());
|
||||
prevState = newState;
|
||||
}else{
|
||||
|
|
|
@ -20,6 +20,8 @@ namespace Mist {
|
|||
virtual bool onFinish(){
|
||||
return stayConnected;
|
||||
}
|
||||
private:
|
||||
std::string origStreamName;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue