mistserver/src/process/process_exec.cpp
Thulinma 0af992d405 Various fixes, among which:
- Fixed segfault when attempting to initialseek on disconnected streams
- Fix 100% CPU bug in controller's stats code
- WebRTC UDP bind socket improvements
- Several segfault fixes
- Increased packet reordering buffer size from 30 to 150 packets
- Tweaks to default output/buffer behaviour for incoming pushes
- Added message for load balancer checks
- Fixed HLS content type
- Stats fixes
- Exit reason fixes
- Fixed socket IP address detection
- Fixed non-string arguments for stream settings
- Added caching for getConnectedBinHost()
- Added WebRTC playback rate control
- Added/completed VP8/VP9 support to WebRTC/RTSP
- Added live seek option to WebRTC
- Fixed seek to exactly newest timestamp
- Fixed HLS input

# Conflicts:
#	lib/defines.h
#	src/input/input.cpp
2021-10-19 22:29:40 +02:00

242 lines
7.2 KiB
C++

#include "process_exec.h"
#include <algorithm> //for std::find
#include <fstream>
#include <mist/procs.h>
#include <mist/tinythread.h>
#include <mist/util.h>
#include <ostream>
#include <sys/stat.h> //for stat
#include <sys/types.h> //for stat
#include <unistd.h> //for stat
int pipein[2], pipeout[2];
Util::Config co;
Util::Config conf;
void sinkThread(void *){
Mist::ProcessSink in(&co);
co.getOption("output", true).append("-");
co.activate();
MEDIUM_MSG("Running sink thread...");
in.setInFile(pipeout[0]);
co.is_active = true;
in.run();
conf.is_active = false;
}
void sourceThread(void *){
Mist::ProcessSource::init(&conf);
conf.getOption("streamname", true).append(Mist::opt["source"].c_str());
conf.getOption("target", true).append("-?audio=all&video=all");
if (Mist::opt.isMember("track_select")){
conf.getOption("target", true).append("-?" + Mist::opt["track_select"].asString());
}
conf.is_active = true;
Socket::Connection c(pipein[1], 0);
Mist::ProcessSource out(c);
MEDIUM_MSG("Running source thread...");
out.run();
co.is_active = false;
}
int main(int argc, char *argv[]){
Util::Config config(argv[0]);
JSON::Value capa;
{
JSON::Value opt;
opt["arg"] = "string";
opt["default"] = "-";
opt["arg_num"] = 1;
opt["help"] = "JSON configuration, or - (default) to read from stdin";
config.addOption("configuration", opt);
opt.null();
opt["long"] = "json";
opt["short"] = "j";
opt["help"] = "Output connector info in JSON format, then exit.";
opt["value"].append(0);
config.addOption("json", opt);
}
capa["codecs"][0u][0u].append("H264");
capa["codecs"][0u][0u].append("HEVC");
capa["codecs"][0u][0u].append("VP8");
capa["codecs"][0u][0u].append("VP9");
capa["codecs"][0u][0u].append("theora");
capa["codecs"][0u][0u].append("MPEG2");
capa["codecs"][0u][0u].append("AV1");
capa["codecs"][0u][1u].append("AAC");
capa["codecs"][0u][1u].append("vorbis");
capa["codecs"][0u][1u].append("opus");
capa["codecs"][0u][1u].append("PCM");
capa["codecs"][0u][1u].append("ALAW");
capa["codecs"][0u][1u].append("ULAW");
capa["codecs"][0u][1u].append("MP2");
capa["codecs"][0u][1u].append("MP3");
capa["codecs"][0u][1u].append("FLOAT");
capa["codecs"][0u][1u].append("AC3");
capa["codecs"][0u][1u].append("DTS");
capa["codecs"][0u][2u].append("+JSON");
if (!(config.parseArgs(argc, argv))){return 1;}
if (config.getBool("json")){
capa["name"] = "MKVExec";
capa["desc"] = "Pipe MKV in, expect MKV out. You choose the executable in between yourself.";
capa["required"]["exec"]["name"] = "Executable";
capa["required"]["exec"]["help"] = "What to executable to run on the stream data";
capa["required"]["exec"]["type"] = "string";
capa["optional"]["sink"]["name"] = "Target stream";
capa["optional"]["sink"]["help"] = "What stream the encoded track should be added to. Defaults "
"to source stream. May contain variables.";
capa["optional"]["sink"]["type"] = "string";
capa["optional"]["sink"]["validate"][0u] = "streamname_with_wildcard_and_variables";
capa["optional"]["track_select"]["name"] = "Source selector(s)";
capa["optional"]["track_select"]["help"] =
"What tracks to select for the input. Defaults to audio=all&video=all.";
capa["optional"]["track_select"]["type"] = "string";
capa["optional"]["track_select"]["validate"][0u] = "track_selector";
capa["optional"]["track_select"]["default"] = "audio=all&video=all";
capa["optional"]["track_inhibit"]["name"] = "Track inhibitor(s)";
capa["optional"]["track_inhibit"]["help"] =
"What tracks to use as inhibitors. If this track selector is able to select a track, the "
"process does not start. Defaults to none.";
capa["optional"]["track_inhibit"]["type"] = "string";
capa["optional"]["track_inhibit"]["validate"][0u] = "track_selector";
capa["optional"]["track_inhibit"]["default"] = "audio=none&video=none&subtitle=none";
std::cout << capa.toString() << std::endl;
return -1;
}
Util::redirectLogsIfNeeded();
// read configuration
if (config.getString("configuration") != "-"){
Mist::opt = JSON::fromString(config.getString("configuration"));
}else{
std::string json, line;
INFO_MSG("Reading configuration from standard input");
while (std::getline(std::cin, line)){json.append(line);}
Mist::opt = JSON::fromString(json.c_str());
}
// check config for generic options
Mist::ProcMKVExec Enc;
if (!Enc.CheckConfig()){
FAIL_MSG("Error config syntax error!");
return 1;
}
// create pipe pair before thread
if (pipe(pipein) || pipe(pipeout)){
FAIL_MSG("Could not create pipes for process!");
return 1;
}
Util::Procs::socketList.insert(pipeout[0]);
Util::Procs::socketList.insert(pipeout[1]);
Util::Procs::socketList.insert(pipein[0]);
Util::Procs::socketList.insert(pipein[1]);
// stream which connects to input
tthread::thread source(sourceThread, 0);
Util::sleep(500);
// needs to pass through encoder to outputEBML
tthread::thread sink(sinkThread, 0);
co.is_active = true;
// run process
Enc.Run();
co.is_active = false;
conf.is_active = false;
// close pipes
close(pipein[0]);
close(pipeout[0]);
close(pipein[1]);
close(pipeout[1]);
sink.join();
HIGH_MSG("sink thread joined")
source.join();
HIGH_MSG("source thread joined");
return 0;
}
namespace Mist{
/// check source, sink, source_track, codec, bitrate, flags and process options.
bool ProcMKVExec::CheckConfig(){
// Check generic configuration variables
if (!opt.isMember("source") || !opt["source"] || !opt["source"].isString()){
FAIL_MSG("invalid source in config!");
return false;
}
if (!opt.isMember("sink") || !opt["sink"] || !opt["sink"].isString()){
INFO_MSG("No sink explicitly set, using source as sink");
}
return true;
}
void ProcMKVExec::Run(){
Util::Procs p;
int ffer = 2;
pid_t execd_proc = -1;
std::string streamName = opt["sink"].asString();
if (!streamName.size()){streamName = opt["source"].asStringRef();}
Util::streamVariables(streamName, opt["source"].asStringRef());
//Do variable substitution on command
std::string tmpCmd = opt["exec"].asStringRef();
Util::streamVariables(tmpCmd, streamName, opt["source"].asStringRef());
// exec command
char exec_cmd[10240];
strncpy(exec_cmd, tmpCmd.c_str(), 10240);
INFO_MSG("Executing command: %s", exec_cmd);
uint8_t argCnt = 0;
char *startCh = 0;
char *args[1280];
for (char *i = exec_cmd; i - exec_cmd < 10240; ++i){
if (!*i){
if (startCh){args[argCnt++] = startCh;}
break;
}
if (*i == ' '){
if (startCh){
args[argCnt++] = startCh;
startCh = 0;
*i = 0;
}
}else{
if (!startCh){startCh = i;}
}
}
args[argCnt] = 0;
execd_proc = p.StartPiped(args, &pipein[0], &pipeout[1], &ffer);
while (conf.is_active && p.isRunning(execd_proc)){Util::sleep(200);}
while (p.isRunning(execd_proc)){
INFO_MSG("Stopping process...");
p.StopAll();
Util::sleep(200);
}
INFO_MSG("Closing process clean");
}
}// namespace Mist