Generalize DTSH header reading and writing; generalize input override prefixes; support external writer targets for pushing

This commit is contained in:
Thulinma 2023-02-17 01:13:29 +01:00
parent 2b18a414b4
commit 0f692233e8
26 changed files with 193 additions and 146 deletions

View file

@ -785,6 +785,28 @@ void Util::Config::addStandardPushCapabilities(JSON::Value &cap){
pp["append"]["format"] = "set_or_unset";
pp["append"]["sort"] = "bf";
pp["split"]["name"] = "Split interval";
pp["split"]["help"] = "Performs a gapless restart of the recording every this many seconds. Always aligns to the next keyframe after this duration, to ensure each recording is fully playable. When set to zero (the default) will not split at all.";
pp["split"]["type"] = "int";
pp["split"]["unit"] = "s";
pp["split"]["sort"] = "bh";
pp["m3u8"]["name"] = "Playlist path (relative to segments)";
pp["m3u8"]["help"] = "If set, will write a m3u8 playlist file for the segments to the given path (relative from the first segment path). When this parameter is used, at least one of the variables $segmentCounter or $currentMediaTime must be part of the segment path (to keep segments from overwriting each other). The \"Split interval\" parameter will default to 60 seconds when using this option.";
pp["m3u8"]["type"] = "string";
pp["m3u8"]["sort"] = "apa";
pp["targetAge"]["name"] = "Playlist target age";
pp["targetAge"]["help"] = "When writing a playlist, delete segment entries that are more than this many seconds old from the playlist (and, if possible, also delete said segments themselves). When set to 0 or left empty, does not delete.";
pp["targetAge"]["type"] = "int";
pp["targetAge"]["unit"] = "s";
pp["targetAge"]["sort"] = "apb";
pp["maxEntries"]["name"] = "Playlist max entries";
pp["maxEntries"]["help"] = "When writing a playlist, delete oldest segment entries once this entry count has been reached (and, if possible, also delete said segments themselves). When set to 0 or left empty, does not delete.";
pp["maxEntries"]["type"] = "int";
pp["maxEntries"]["sort"] = "apc";
pp["pushdelay"]["name"] = "Push delay";
pp["pushdelay"]["help"] = "Ensures the stream is always delayed by at least this many seconds. Internally overrides the \"realtime\" and \"start\" parameters";
pp["pushdelay"]["type"] = "int";
@ -793,12 +815,6 @@ void Util::Config::addStandardPushCapabilities(JSON::Value &cap){
pp["pushdelay"]["disable"].append("start");
pp["pushdelay"]["sort"] = "bg";
pp["split"]["name"] = "Split interval";
pp["split"]["help"] = "Performs a gapless restart of the recording every this may seconds. Always aligns to the next keyframe after this duration, to ensure each recording is fully playable";
pp["split"]["type"] = "int";
pp["split"]["unit"] = "s";
pp["split"]["sort"] = "bh";
pp["duration"]["name"] = "Duration of push";
pp["duration"]["help"] = "How much media time to push, in seconds. Internally overrides \"recstop\"";
pp["duration"]["type"] = "int";

View file

@ -12,6 +12,7 @@
#include "procs.h"
#include "shared_memory.h"
#include "socket.h"
#include "url.h"
#include "stream.h"
#include "triggers.h" //LTS
#include <semaphore.h>
@ -673,6 +674,25 @@ JSON::Value Util::getInputBySource(const std::string &filename, bool isProvider)
for (unsigned int i = 0; i < input_size; ++i){
DTSC::Scan tmp_input = inputs.getIndice(i);
// if name prefix based match, always force 99 priority
if (tmp_input.getMember("name")){
std::string inPrefix = tmp_input.getMember("name").asString() + ":";
if (tmpFn.size() > inPrefix.size()){
Util::stringToLower(inPrefix);
std::string fnPrefix = tmpFn.substr(0, inPrefix.size());
Util::stringToLower(fnPrefix);
if (inPrefix == fnPrefix){
if (tmp_input.getMember("non-provider") && !isProvider){
noProviderNoPick = true;
continue;
}
curPrio = 99;
selected = true;
input = tmp_input;
}
}
}
// if match voor current stream && priority is hoger dan wat we al hebben
if (tmp_input.getMember("source_match") && curPrio < tmp_input.getMember("priority").asInt()){
if (tmp_input.getMember("source_match").getSize()){
@ -769,18 +789,43 @@ pid_t Util::startPush(const std::string &streamname, std::string &target, int de
std::string back = tar_match.substr(tar_match.find('*') + 1);
MEDIUM_MSG("Checking output %s: %s (%s)", outputs.getIndiceName(i).c_str(),
output.getMember("name").asString().c_str(), checkTarget.c_str());
if (checkTarget.substr(0, front.size()) == front &&
checkTarget.substr(checkTarget.size() - back.size()) == back){
output_bin = Util::getMyPath() + "MistOut" + output.getMember("name").asString();
break;
}
//Check for external writer support
if (front == "/" && back.size() && checkTarget.substr(checkTarget.size() - back.size()) == back){
HTTP::URL tUri(target);
// If it is a remote target, we might need to spawn an external binary
if (tUri.isLocalPath()){continue;}
// Read configured external writers
IPC::sharedPage extwriPage(EXTWRITERS, 0, false, false);
if (extwriPage.mapped){
Util::RelAccX extWri(extwriPage.mapped, false);
if (extWri.isReady()){
for (uint64_t i = 0; i < extWri.getEndPos(); i++){
Util::RelAccX protocols = Util::RelAccX(extWri.getPointer("protocols", i));
uint8_t protocolCount = protocols.getPresent();
JSON::Value protocolArray;
for (uint8_t idx = 0; idx < protocolCount; idx++){
if (tUri.protocol == protocols.getPointer("protocol", idx)){
output_bin = Util::getMyPath() + "MistOut" + output.getMember("name").asString();
break;
}
if (output_bin.size()){break;}
}
if (output_bin.size()){break;}
}
}
}
}
}
}
}
}
if (output_bin == ""){
if (!output_bin.size()){
FAIL_MSG("No output found for target %s, aborting push.", target.c_str());
return 0;
}