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"]["format"] = "set_or_unset";
pp["append"]["sort"] = "bf"; 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"]["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"]["help"] = "Ensures the stream is always delayed by at least this many seconds. Internally overrides the \"realtime\" and \"start\" parameters";
pp["pushdelay"]["type"] = "int"; pp["pushdelay"]["type"] = "int";
@ -793,12 +815,6 @@ void Util::Config::addStandardPushCapabilities(JSON::Value &cap){
pp["pushdelay"]["disable"].append("start"); pp["pushdelay"]["disable"].append("start");
pp["pushdelay"]["sort"] = "bg"; 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"]["name"] = "Duration of push";
pp["duration"]["help"] = "How much media time to push, in seconds. Internally overrides \"recstop\""; pp["duration"]["help"] = "How much media time to push, in seconds. Internally overrides \"recstop\"";
pp["duration"]["type"] = "int"; pp["duration"]["type"] = "int";

View file

@ -12,6 +12,7 @@
#include "procs.h" #include "procs.h"
#include "shared_memory.h" #include "shared_memory.h"
#include "socket.h" #include "socket.h"
#include "url.h"
#include "stream.h" #include "stream.h"
#include "triggers.h" //LTS #include "triggers.h" //LTS
#include <semaphore.h> #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){ for (unsigned int i = 0; i < input_size; ++i){
DTSC::Scan tmp_input = inputs.getIndice(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 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") && curPrio < tmp_input.getMember("priority").asInt()){
if (tmp_input.getMember("source_match").getSize()){ 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); std::string back = tar_match.substr(tar_match.find('*') + 1);
MEDIUM_MSG("Checking output %s: %s (%s)", outputs.getIndiceName(i).c_str(), MEDIUM_MSG("Checking output %s: %s (%s)", outputs.getIndiceName(i).c_str(),
output.getMember("name").asString().c_str(), checkTarget.c_str()); output.getMember("name").asString().c_str(), checkTarget.c_str());
if (checkTarget.substr(0, front.size()) == front && if (checkTarget.substr(0, front.size()) == front &&
checkTarget.substr(checkTarget.size() - back.size()) == back){ checkTarget.substr(checkTarget.size() - back.size()) == back){
output_bin = Util::getMyPath() + "MistOut" + output.getMember("name").asString(); output_bin = Util::getMyPath() + "MistOut" + output.getMember("name").asString();
break; 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()); FAIL_MSG("No output found for target %s, aborting push.", target.c_str());
return 0; return 0;
} }

View file

@ -282,6 +282,17 @@ namespace Controller{
WARN_MSG("Input %s version mismatch (%s != " PACKAGE_VERSION ")", entryName.c_str(), WARN_MSG("Input %s version mismatch (%s != " PACKAGE_VERSION ")", entryName.c_str(),
capabilities["inputs"][entryName]["version"].asStringRef().c_str()); capabilities["inputs"][entryName]["version"].asStringRef().c_str());
capabilities["inputs"].removeMember(entryName); capabilities["inputs"].removeMember(entryName);
}else{
JSON::Value & inRef = capabilities["inputs"][entryName];
if (inRef.isMember("source_match") && inRef.isMember("name")){
if (!inRef["source_match"].isArray()){
std::string m = inRef["source_match"].asString();
inRef["source_match"].append(m);
}
std::string n = inRef["name"].asString();
Util::stringToLower(n);
inRef["source_match"].append(n+":*");
}
} }
} }
} }

View file

@ -420,33 +420,10 @@ namespace Controller{
return ret; return ret;
} }
bool isMatch(const std::string &source, const std::string &match){
std::string front = match.substr(0, match.find('*'));
std::string back = match.substr(match.find('*') + 1);
// if the length of the source is smaller than the front and back matching parts together, it can never match
if (source.size() < front.size() + back.size()){return false;}
return (source.substr(0, front.size()) == front && source.substr(source.size() - back.size()) == back);
}
void checkParameters(JSON::Value &streamObj){ void checkParameters(JSON::Value &streamObj){
JSON::Value &inpt = Controller::capabilities["inputs"]; JSON::Value in = Util::getInputBySource(streamObj["source"].asStringRef(), true);
std::string match; if (in){
jsonForEach(inpt, it){ jsonForEach(in["hardcoded"], it){streamObj[it.key()] = *it;}
if ((*it)["source_match"].isArray()){
jsonForEach((*it)["source_match"], subIt){
if (isMatch(streamObj["source"].asStringRef(), (*subIt).asStringRef())){
match = (*it)["name"].asString();
}
}
}
if ((*it)["source_match"].isString()){
if (isMatch(streamObj["source"].asStringRef(), (*it)["source_match"].asStringRef())){
match = (*it)["name"].asString();
}
}
}
if (match != ""){
jsonForEach(inpt[match]["hardcoded"], it){streamObj[it.key()] = *it;}
} }
} }

View file

@ -309,6 +309,45 @@ namespace Mist{
INFO_MSG("Input booting"); INFO_MSG("Input booting");
//Check if the input uses the name-based-override, and strip it
{
std::string input = config->getString("input");
std::string prefix = capa["name"].asStringRef() + ":";
Util::stringToLower(prefix);
if (input.size() > prefix.size()){
std::string match = input.substr(0, prefix.size());
Util::stringToLower(match);
if (prefix == match){
//We have a prefix match - make sure we don't _also_ have a proper source_match
bool source_match = false;
if (capa["source_match"].size()){
jsonForEach(capa["source_match"], it){
const std::string & source = it->asStringRef();
std::string front = source.substr(0, source.find('*'));
std::string back = source.substr(source.find('*') + 1);
if (input.size() > front.size()+back.size() && input.substr(0, front.size()) == front && input.substr(input.size() - back.size()) == back){
source_match = true;
break;
}
}
}else{
const std::string & source = capa["source_match"].asStringRef();
std::string front = source.substr(0, source.find('*'));
std::string back = source.substr(source.find('*') + 1);
if (input.size() > front.size()+back.size() && input.substr(0, front.size()) == front && input.substr(input.size() - back.size()) == back){
source_match = true;
}
}
//Only if no source_match, strip the prefix from the input string
if (!source_match){
config->getOption("input", true).append(input.substr(prefix.size()));
}
}
}
}
if (!checkArguments()){ if (!checkArguments()){
FAIL_MSG("Setup failed - exiting"); FAIL_MSG("Setup failed - exiting");
return 0; return 0;
@ -505,16 +544,19 @@ namespace Mist{
Comms::sessionConfigCache(); Comms::sessionConfigCache();
if (streamStatus){streamStatus.mapped[0] = STRMSTAT_BOOT;} if (streamStatus){streamStatus.mapped[0] = STRMSTAT_BOOT;}
checkHeaderTimes(config->getString("input")); checkHeaderTimes(config->getString("input"));
//needHeader internally calls readExistingHeader which in turn attempts to read header cache
if (needHeader()){ if (needHeader()){
uint64_t timer = Util::bootMS(); uint64_t timer = Util::getMicros();
bool headerSuccess = readHeader(); if (!readHeader() || (!M && needsLock())){
if (!headerSuccess || (!M && needsLock())){
FAIL_MSG("Reading header for '%s' failed.", config->getString("input").c_str()); FAIL_MSG("Reading header for '%s' failed.", config->getString("input").c_str());
return 0; return 0;
} }
timer = Util::bootMS() - timer; timer = Util::getMicros(timer);
INFO_MSG("Read header in %" PRIu64 "ms (%zu tracks)", timer, M?M.trackCount():(size_t)0); INFO_MSG("Created header in %.3f ms (%zu tracks)", (double)timer/1000.0, M?M.trackCount():(size_t)0);
//Write header to file for caching purposes
M.toFile(config->getString("input") + ".dtsh");
} }
postHeader();
if (config->getBool("headeronly")){return 0;} if (config->getBool("headeronly")){return 0;}
if (M && M.getVod()){ if (M && M.getVod()){
meta.removeEmptyTracks(); meta.removeEmptyTracks();
@ -1089,10 +1131,12 @@ namespace Mist{
return r.str(); return r.str();
} }
/// Attempts to create a header.
/// Returns true on success.
/// Default implementation fails and prints a warning.
bool Input::readHeader(){ bool Input::readHeader(){
INFO_MSG("Empty header created by default readHeader handler"); WARN_MSG("Default readHeader implementation called - this is not expected to happen");
meta.reInit(streamName); return false;
return true;
} }
void Input::parseHeader(){ void Input::parseHeader(){
@ -1476,15 +1520,7 @@ namespace Mist{
} }
bool Input::readExistingHeader(){ bool Input::readExistingHeader(){
if (config->getBool("realtime")){ if (!config->getBool("realtime")){
meta.reInit("", config->getString("input") + ".dtsh");
if (!meta){return false;}
if (meta.version != DTSH_VERSION){
INFO_MSG("Updating wrong version header file from version %u to %u", meta.version, DTSH_VERSION);
return false;
}
return meta;
}
char pageName[NAME_BUFFER_SIZE]; char pageName[NAME_BUFFER_SIZE];
snprintf(pageName, NAME_BUFFER_SIZE, SHM_STREAM_META, config->getString("streamname").c_str()); snprintf(pageName, NAME_BUFFER_SIZE, SHM_STREAM_META, config->getString("streamname").c_str());
IPC::sharedPage sp(pageName, 0, false, false); IPC::sharedPage sp(pageName, 0, false, false);
@ -1497,6 +1533,7 @@ namespace Mist{
return true; return true;
} }
} }
}
// Try to read any existing DTSH file // Try to read any existing DTSH file
std::string fileName = config->getString("input") + ".dtsh"; std::string fileName = config->getString("input") + ".dtsh";
HIGH_MSG("Loading metadata for stream '%s' from file '%s'", streamName.c_str(), fileName.c_str()); HIGH_MSG("Loading metadata for stream '%s' from file '%s'", streamName.c_str(), fileName.c_str());
@ -1509,7 +1546,7 @@ namespace Mist{
if (!fileSize){return false;} if (!fileSize){return false;}
DTSC::Packet pkt(scanBuf, fileSize, true); DTSC::Packet pkt(scanBuf, fileSize, true);
HIGH_MSG("Retrieved header of %lu bytes", fileSize); HIGH_MSG("Retrieved header of %lu bytes", fileSize);
meta.reInit(streamName, pkt.getScan()); meta.reInit(config->getBool("realtime") ? "" : streamName, pkt.getScan());
if (meta.version != DTSH_VERSION){ if (meta.version != DTSH_VERSION){
INFO_MSG("Updating wrong version header file from version %u to %u", meta.version, DTSH_VERSION); INFO_MSG("Updating wrong version header file from version %u to %u", meta.version, DTSH_VERSION);

View file

@ -40,6 +40,7 @@ namespace Mist{
virtual bool checkArguments() = 0; virtual bool checkArguments() = 0;
virtual bool readHeader(); virtual bool readHeader();
virtual bool needHeader(){return !readExistingHeader();} virtual bool needHeader(){return !readExistingHeader();}
virtual void postHeader(){};
virtual bool preRun(){return true;} virtual bool preRun(){return true;}
virtual bool isThread(){return false;} virtual bool isThread(){return false;}
virtual bool isSingular(){return !config->getBool("realtime");} virtual bool isSingular(){return !config->getBool("realtime");}

View file

@ -204,8 +204,6 @@ namespace Mist{
if (!inFile.seek(0)) if (!inFile.seek(0))
ERROR_MSG("Could not seek back to position 0!"); ERROR_MSG("Could not seek back to position 0!");
thisTime = 0; thisTime = 0;
M.toFile(config->getString("input") + ".dtsh");
return true; return true;
} }

View file

@ -143,7 +143,7 @@ namespace Mist{
meta.setType(idx, "audio"); meta.setType(idx, "audio");
meta.setRate(idx, strm->codecpar->sample_rate); meta.setRate(idx, strm->codecpar->sample_rate);
meta.setSize(idx, strm->codecpar->frame_size); meta.setSize(idx, strm->codecpar->frame_size);
meta.setChannels(idx, strm->codecpar->channels); meta.setChannels(idx, strm->codecpar->ch_layout.nb_channels);
} }
} }

View file

@ -9,7 +9,6 @@ namespace Mist{
protected: protected:
bool checkArguments(){return false;}; bool checkArguments(){return false;};
bool readHeader(){return false;};
bool needHeader(){return false;}; bool needHeader(){return false;};
}; };
}// namespace Mist }// namespace Mist

View file

@ -31,7 +31,6 @@ namespace Mist{
bool preRun(); bool preRun();
bool checkArguments(){return true;} bool checkArguments(){return true;}
void updateMeta(); void updateMeta();
bool readHeader(){return false;}
bool needHeader(){return false;} bool needHeader(){return false;}
void getNext(size_t idx = INVALID_TRACK_ID){}; void getNext(size_t idx = INVALID_TRACK_ID){};
void seek(uint64_t seekTime, size_t idx = INVALID_TRACK_ID){}; void seek(uint64_t seekTime, size_t idx = INVALID_TRACK_ID){};

View file

@ -237,10 +237,8 @@ namespace Mist{
bool inputDTSC::readHeader(){ bool inputDTSC::readHeader(){
if (!F){return false;} if (!F){return false;}
if (!readExistingHeader()){
size_t moreHeader = 0; size_t moreHeader = 0;
do{ do{
// read existing header from file here?
char hdr[8]; char hdr[8];
fseek(F, moreHeader, SEEK_SET); fseek(F, moreHeader, SEEK_SET);
if (fread(hdr, 8, 1, F) != 1){ if (fread(hdr, 8, 1, F) != 1){
@ -268,8 +266,6 @@ namespace Mist{
free(pkt); free(pkt);
}while (moreHeader); }while (moreHeader);
}
return meta; return meta;
} }

View file

@ -194,13 +194,6 @@ namespace Mist{
bool InputEBML::readExistingHeader(){ bool InputEBML::readExistingHeader(){
if (!Input::readExistingHeader()){return false;} if (!Input::readExistingHeader()){return false;}
std::set<size_t> validTracks = M.getValidTracks();
for (std::set<size_t>::iterator it = validTracks.begin(); it != validTracks.end(); it++){
if (M.getCodec(*it) == "PCMLE"){
meta.setCodec(*it, "PCM");
swapEndianness.insert(*it);
}
}
if (M.inputLocalVars.isMember("timescale")){ if (M.inputLocalVars.isMember("timescale")){
timeScale = ((double)M.inputLocalVars["timescale"].asInt()) / 1000000.0; timeScale = ((double)M.inputLocalVars["timescale"].asInt()) / 1000000.0;
} }
@ -213,8 +206,6 @@ namespace Mist{
bool InputEBML::readHeader(){ bool InputEBML::readHeader(){
if (!inFile){return false;} if (!inFile){return false;}
// Create header file from file
uint64_t bench = Util::getMicros();
if (!meta || (needsLock() && isSingular())){ if (!meta || (needsLock() && isSingular())){
meta.reInit(isSingular() ? streamName : ""); meta.reInit(isSingular() ? streamName : "");
} }
@ -462,12 +453,13 @@ namespace Mist{
} }
meta.inputLocalVars["version"] = 2; meta.inputLocalVars["version"] = 2;
bench = Util::getMicros(bench);
INFO_MSG("Header generated in %" PRIu64 " ms", bench / 1000);
clearPredictors(); clearPredictors();
bufferedPacks = 0; bufferedPacks = 0;
M.toFile(config->getString("input") + ".dtsh"); return true;
}
void InputEBML::postHeader(){
//Record PCMLE tracks as being PCM with swapped endianness
std::set<size_t> validTracks = M.getValidTracks(); std::set<size_t> validTracks = M.getValidTracks();
for (std::set<size_t>::iterator it = validTracks.begin(); it != validTracks.end(); it++){ for (std::set<size_t>::iterator it = validTracks.begin(); it != validTracks.end(); it++){
if (M.getCodec(*it) == "PCMLE"){ if (M.getCodec(*it) == "PCMLE"){
@ -475,7 +467,6 @@ namespace Mist{
swapEndianness.insert(*it); swapEndianness.insert(*it);
} }
} }
return true;
} }
void InputEBML::fillPacket(packetData &C){ void InputEBML::fillPacket(packetData &C){

View file

@ -135,6 +135,7 @@ namespace Mist{
bool checkArguments(); bool checkArguments();
bool preRun(); bool preRun();
bool readHeader(); bool readHeader();
void postHeader();
bool readElement(); bool readElement();
void getNext(size_t idx = INVALID_TRACK_ID); void getNext(size_t idx = INVALID_TRACK_ID);
void seek(uint64_t seekTime, size_t idx = INVALID_TRACK_ID); void seek(uint64_t seekTime, size_t idx = INVALID_TRACK_ID);

View file

@ -79,7 +79,6 @@ namespace Mist{
bool inputFLV::readHeader(){ bool inputFLV::readHeader(){
if (!inFile){return false;} if (!inFile){return false;}
if (readExistingHeader()){return true;}
meta.reInit(isSingular() ? streamName : ""); meta.reInit(isSingular() ? streamName : "");
// Create header file from FLV data // Create header file from FLV data
Util::fseek(inFile, 13, SEEK_SET); Util::fseek(inFile, 13, SEEK_SET);
@ -107,7 +106,6 @@ namespace Mist{
FLV::Parse_Error = false; FLV::Parse_Error = false;
ERROR_MSG("Stopping at FLV parse error @%" PRIu64 ": %s", lastBytePos, FLV::Error_Str.c_str()); ERROR_MSG("Stopping at FLV parse error @%" PRIu64 ": %s", lastBytePos, FLV::Error_Str.c_str());
} }
M.toFile(config->getString("input") + ".dtsh");
Util::fseek(inFile, 13, SEEK_SET); Util::fseek(inFile, 13, SEEK_SET);
return true; return true;
} }

View file

@ -9,7 +9,6 @@ namespace Mist{
protected: protected:
bool checkArguments(){return false;}; bool checkArguments(){return false;};
bool readHeader(){return false;};
bool needHeader(){return false;}; bool needHeader(){return false;};
void getNext(size_t idx = INVALID_TRACK_ID){} void getNext(size_t idx = INVALID_TRACK_ID){}
void seek(uint64_t time, size_t idx = INVALID_TRACK_ID){} void seek(uint64_t time, size_t idx = INVALID_TRACK_ID){}

View file

@ -16,7 +16,6 @@ namespace Mist{
std::string spsInfo; std::string spsInfo;
uint64_t frameCount; uint64_t frameCount;
// Empty defaults // Empty defaults
bool readHeader(){return true;}
bool openStreamSource(); bool openStreamSource();
void closeStreamSource(){} void closeStreamSource(){}
void parseStreamHeader(); void parseStreamHeader();

View file

@ -763,7 +763,6 @@ namespace Mist{
bool inputHLS::readHeader(){ bool inputHLS::readHeader(){
if (streamIsLive && !isLiveDVR){return true;} if (streamIsLive && !isLiveDVR){return true;}
if (readExistingHeader()){return true;}
// to analyse and extract data // to analyse and extract data
TS::Packet packet; TS::Packet packet;
char *data; char *data;
@ -891,10 +890,6 @@ namespace Mist{
thisMappingsR[JSON::Value(pidIt->first).asString()] = pidIt->second; thisMappingsR[JSON::Value(pidIt->first).asString()] = pidIt->second;
} }
meta.inputLocalVars["pidMappingR"] = thisMappingsR; meta.inputLocalVars["pidMappingR"] = thisMappingsR;
INFO_MSG("write header file...");
M.toFile((config->getString("input") + ".dtsh").c_str());
return true; return true;
} }

View file

@ -87,7 +87,6 @@ namespace Mist{
} }
curBytePos = ftell(inFile); curBytePos = ftell(inFile);
} }
M.toFile(config->getString("input") + ".dtsh");
return true; return true;
} }

View file

@ -85,7 +85,6 @@ namespace Mist{
fseek(inFile, 0, SEEK_SET); fseek(inFile, 0, SEEK_SET);
timestamp = 0; timestamp = 0;
M.toFile(config->getString("input") + ".dtsh");
return true; return true;
} }

View file

@ -112,7 +112,6 @@ namespace Mist{
capa["source_match"].append("https://*.mp4"); capa["source_match"].append("https://*.mp4");
capa["source_match"].append("s3+http://*.mp4"); capa["source_match"].append("s3+http://*.mp4");
capa["source_match"].append("s3+https://*.mp4"); capa["source_match"].append("s3+https://*.mp4");
capa["source_match"].append("mp4:*");
capa["source_file"] = "$source"; capa["source_file"] = "$source";
capa["priority"] = 9; capa["priority"] = 9;
capa["codecs"]["video"].append("HEVC"); capa["codecs"]["video"].append("HEVC");
@ -147,9 +146,7 @@ namespace Mist{
bool inputMP4::preRun(){ bool inputMP4::preRun(){
// open File // open File
std::string inUrl = config->getString("input"); inFile.open(config->getString("input"));
if (inUrl.size() > 4 && inUrl.substr(0, 4) == "mp4:"){inUrl.erase(0, 4);}
inFile.open(inUrl);
if (!inFile){return false;} if (!inFile){return false;}
if (!inFile.isSeekable()){ if (!inFile.isSeekable()){
FAIL_MSG("MP4 input only supports seekable data sources, for now, and this source is not seekable: %s", config->getString("input").c_str()); FAIL_MSG("MP4 input only supports seekable data sources, for now, and this source is not seekable: %s", config->getString("input").c_str());
@ -160,6 +157,13 @@ namespace Mist{
void inputMP4::dataCallback(const char *ptr, size_t size){readBuffer.append(ptr, size);} void inputMP4::dataCallback(const char *ptr, size_t size){readBuffer.append(ptr, size);}
bool inputMP4::needHeader(){
//Attempt to read cache, but force calling of the readHeader function anyway
bool r = Input::needHeader();
if (!r){r = !readHeader();}
return r;
}
bool inputMP4::readHeader(){ bool inputMP4::readHeader(){
if (!inFile){ if (!inFile){
Util::logExitReason("Could not open input file"); Util::logExitReason("Could not open input file");
@ -221,14 +225,13 @@ namespace Mist{
} }
// See whether a separate header file exists. // If we already read a cached header, we can exit here.
if (readExistingHeader()){ if (M){
bps = 0; bps = 0;
std::set<size_t> tracks = M.getValidTracks(); std::set<size_t> tracks = M.getValidTracks();
for (std::set<size_t>::iterator it = tracks.begin(); it != tracks.end(); it++){bps += M.getBps(*it);} for (std::set<size_t>::iterator it = tracks.begin(); it != tracks.end(); it++){bps += M.getBps(*it);}
return true; return true;
} }
INFO_MSG("Not reading existing header");
meta.reInit(isSingular() ? streamName : ""); meta.reInit(isSingular() ? streamName : "");
tNumber = 0; tNumber = 0;
@ -459,14 +462,6 @@ namespace Mist{
} }
} }
// outputting dtsh file
std::string inUrl = config->getString("input");
if (inUrl.size() > 4 && inUrl.substr(0, 4) == "mp4:"){inUrl.erase(0, 4);}
if (inUrl != "-" && HTTP::URL(inUrl).isLocalPath()){
M.toFile(inUrl + ".dtsh");
}else{
INFO_MSG("Skipping header write, as the source is not a local file");
}
bps = 0; bps = 0;
std::set<size_t> tracks = M.getValidTracks(); std::set<size_t> tracks = M.getValidTracks();
for (std::set<size_t>::iterator it = tracks.begin(); it != tracks.end(); it++){bps += M.getBps(*it);} for (std::set<size_t>::iterator it = tracks.begin(); it != tracks.end(); it++){bps += M.getBps(*it);}

View file

@ -80,7 +80,7 @@ namespace Mist{
bool checkArguments(); bool checkArguments();
bool preRun(); bool preRun();
bool readHeader(); bool readHeader();
bool needHeader(){return true;} bool needHeader();
void getNext(size_t idx = INVALID_TRACK_ID); void getNext(size_t idx = INVALID_TRACK_ID);
void seek(uint64_t seekTime, size_t idx = INVALID_TRACK_ID); void seek(uint64_t seekTime, size_t idx = INVALID_TRACK_ID);
void handleSeek(uint64_t seekTime, size_t idx); void handleSeek(uint64_t seekTime, size_t idx);

View file

@ -222,8 +222,6 @@ namespace Mist{
meta.update(thisPacket); meta.update(thisPacket);
getNext(); getNext();
} }
meta.toFile(config->getString("input") + ".dtsh");
return true; return true;
} }

View file

@ -26,7 +26,6 @@ namespace Mist{
// Private Functions // Private Functions
bool checkArguments(); bool checkArguments();
bool needHeader(){return false;} bool needHeader(){return false;}
bool readHeader(){return true;}
bool openStreamSource(); bool openStreamSource();
void closeStreamSource(); void closeStreamSource();
void parseStreamHeader(); void parseStreamHeader();

View file

@ -26,7 +26,6 @@ namespace Mist{
bool checkArguments(); bool checkArguments();
// Overwrite default functions from input // Overwrite default functions from input
bool needHeader(){return false;} bool needHeader(){return false;}
bool readHeader(){return true;}
// Force to stream > serve // Force to stream > serve
bool needsLock(){return false;} bool needsLock(){return false;}
// Open connection with input // Open connection with input

View file

@ -57,9 +57,6 @@ namespace Mist{
meta.update(thisPacket); meta.update(thisPacket);
getNext(); getNext();
} }
// outputting dtsh file
M.toFile(config->getString("input") + ".dtsh");
return true; return true;
} }

View file

@ -413,7 +413,6 @@ namespace Mist{
} }
fseek(inFile, 0, SEEK_SET); fseek(inFile, 0, SEEK_SET);
meta.toFile(config->getString("input") + ".dtsh");
return true; return true;
} }