Generalize DTSH header reading and writing; generalize input override prefixes; support external writer targets for pushing
This commit is contained in:
parent
2b18a414b4
commit
0f692233e8
26 changed files with 193 additions and 146 deletions
|
@ -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";
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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+":*");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,25 +1520,18 @@ namespace Mist{
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Input::readExistingHeader(){
|
bool Input::readExistingHeader(){
|
||||||
if (config->getBool("realtime")){
|
if (!config->getBool("realtime")){
|
||||||
meta.reInit("", config->getString("input") + ".dtsh");
|
char pageName[NAME_BUFFER_SIZE];
|
||||||
if (!meta){return false;}
|
snprintf(pageName, NAME_BUFFER_SIZE, SHM_STREAM_META, config->getString("streamname").c_str());
|
||||||
if (meta.version != DTSH_VERSION){
|
IPC::sharedPage sp(pageName, 0, false, false);
|
||||||
INFO_MSG("Updating wrong version header file from version %u to %u", meta.version, DTSH_VERSION);
|
if (sp){
|
||||||
return false;
|
sp.close();
|
||||||
}
|
meta.reInit(config->getString("streamname"), false);
|
||||||
return meta;
|
if (meta){
|
||||||
}
|
meta.setMaster(true);
|
||||||
char pageName[NAME_BUFFER_SIZE];
|
INFO_MSG("Read existing header");
|
||||||
snprintf(pageName, NAME_BUFFER_SIZE, SHM_STREAM_META, config->getString("streamname").c_str());
|
return true;
|
||||||
IPC::sharedPage sp(pageName, 0, false, false);
|
}
|
||||||
if (sp){
|
|
||||||
sp.close();
|
|
||||||
meta.reInit(config->getString("streamname"), false);
|
|
||||||
if (meta){
|
|
||||||
meta.setMaster(true);
|
|
||||||
INFO_MSG("Read existing header");
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Try to read any existing DTSH file
|
// Try to read any existing DTSH file
|
||||||
|
@ -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);
|
||||||
|
|
|
@ -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");}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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){};
|
||||||
|
|
|
@ -237,39 +237,35 @@ 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{
|
char hdr[8];
|
||||||
// read existing header from file here?
|
fseek(F, moreHeader, SEEK_SET);
|
||||||
char hdr[8];
|
if (fread(hdr, 8, 1, F) != 1){
|
||||||
fseek(F, moreHeader, SEEK_SET);
|
FAIL_MSG("Could not read header @ bpos %zu", moreHeader);
|
||||||
if (fread(hdr, 8, 1, F) != 1){
|
return false;
|
||||||
FAIL_MSG("Could not read header @ bpos %zu", moreHeader);
|
}
|
||||||
return false;
|
if (memcmp(hdr, DTSC::Magic_Header, 4)){
|
||||||
}
|
FAIL_MSG("File does not have a DTSC header @ bpos %zu", moreHeader);
|
||||||
if (memcmp(hdr, DTSC::Magic_Header, 4)){
|
return false;
|
||||||
FAIL_MSG("File does not have a DTSC header @ bpos %zu", moreHeader);
|
}
|
||||||
return false;
|
size_t pktLen = Bit::btohl(hdr + 4);
|
||||||
}
|
char *pkt = (char *)malloc(8 + pktLen * sizeof(char));
|
||||||
size_t pktLen = Bit::btohl(hdr + 4);
|
fseek(F, moreHeader, SEEK_SET);
|
||||||
char *pkt = (char *)malloc(8 + pktLen * sizeof(char));
|
if (fread(pkt, 8 + pktLen, 1, F) != 1){
|
||||||
fseek(F, moreHeader, SEEK_SET);
|
|
||||||
if (fread(pkt, 8 + pktLen, 1, F) != 1){
|
|
||||||
free(pkt);
|
|
||||||
FAIL_MSG("Could not read packet @ bpos %zu", moreHeader);
|
|
||||||
}
|
|
||||||
DTSC::Scan S(pkt + 8, pktLen);
|
|
||||||
if (S.hasMember("moreheader") && S.getMember("moreheader").asInt()){
|
|
||||||
moreHeader = S.getMember("moreheader").asInt();
|
|
||||||
}else{
|
|
||||||
moreHeader = 0;
|
|
||||||
meta.reInit(isSingular() ? streamName : "", S);
|
|
||||||
}
|
|
||||||
|
|
||||||
free(pkt);
|
free(pkt);
|
||||||
}while (moreHeader);
|
FAIL_MSG("Could not read packet @ bpos %zu", moreHeader);
|
||||||
}
|
}
|
||||||
|
DTSC::Scan S(pkt + 8, pktLen);
|
||||||
|
if (S.hasMember("moreheader") && S.getMember("moreheader").asInt()){
|
||||||
|
moreHeader = S.getMember("moreheader").asInt();
|
||||||
|
}else{
|
||||||
|
moreHeader = 0;
|
||||||
|
meta.reInit(isSingular() ? streamName : "", S);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(pkt);
|
||||||
|
}while (moreHeader);
|
||||||
return meta;
|
return meta;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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){
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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){}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -87,7 +87,6 @@ namespace Mist{
|
||||||
}
|
}
|
||||||
curBytePos = ftell(inFile);
|
curBytePos = ftell(inFile);
|
||||||
}
|
}
|
||||||
M.toFile(config->getString("input") + ".dtsh");
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -222,8 +222,6 @@ namespace Mist{
|
||||||
meta.update(thisPacket);
|
meta.update(thisPacket);
|
||||||
getNext();
|
getNext();
|
||||||
}
|
}
|
||||||
|
|
||||||
meta.toFile(config->getString("input") + ".dtsh");
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue