From 9dc2947c084e4eaddd9185f5ba70dbf18a6f9bd7 Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Sat, 21 Sep 2024 18:06:12 -0400 Subject: [PATCH] Using an FSM to keep the server straight and detect errors in it. --- main.cpp | 111 +++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 91 insertions(+), 20 deletions(-) diff --git a/main.cpp b/main.cpp index 8142423..abfc11b 100644 --- a/main.cpp +++ b/main.cpp @@ -1,45 +1,116 @@ +#define FSM_DEBUG 1 #include #include #include #include "dbc.hpp" +#include "fsm.hpp" #define BUF_MAX 1024 * 10 using namespace fmt; using namespace nlohmann; -FILE *run_ffmpeg(const string listen_at, const string bitrate, const string send_to) { +struct Config { + json json_config; + string listen_at = ""; + string bitrate = ""; + string send_to = ""; + + Config(const string json_file) { + load(json_file); + } + + void load(const string file_name) { + std::ifstream infile(file_name); + json_config = json::parse(infile); + + listen_at = json_config["listen_at"].template get(); + bitrate = json_config["bitrate"].template get(); + send_to = json_config["send_to"].template get(); + } +}; + +FILE *run_ffmpeg(Config &config) { string ffmpeg_cmd = format("ffmpeg -listen 1 -i {} -bufsize 3000k -maxrate {} -flags +global_header -c:v libx264 -preset veryfast -tune zerolatency -g:v 60 -vb {} -c:a copy -f flv {}", - listen_at, bitrate, bitrate, send_to); + config.listen_at, + config.bitrate, + config.bitrate, + config.send_to); println("RUNNING: {}", ffmpeg_cmd); - FILE *handle = popen(ffmpeg_cmd.c_str(), "r"); - dbc::check(handle != nullptr, "failed to start ffmpeg"); - return handle; + return popen(ffmpeg_cmd.c_str(), "r"); } -json load_config(const string file_name) { - std::ifstream infile(file_name); - return json::parse(infile); -} -int main() { +enum class ServerState { + START, READING, STOP, ERROR +}; + +enum class ServerEvent { + GO +}; + +class Server : DeadSimpleFSM { + Config &config; FILE *handle = nullptr; char buffer[BUF_MAX]; char *res = nullptr; - json config = load_config("config.json"); - const string listen_at = config["listen_at"].template get(); - const string bitrate = config["bitrate"].template get(); - const string send_to = config["send_to"].template get(); +public: - handle = run_ffmpeg(listen_at, bitrate, send_to); + Server(Config &config) : config(config) {}; - do { + bool stopped() { + return in_state(ServerState::STOP) || in_state(ServerState::ERROR); + } + + void event(ServerEvent ev) { + switch(_state) { + FSM_STATE(ServerState, START, ev); + FSM_STATE(ServerState, READING, ev); + FSM_STATE(ServerState, STOP, ev); + FSM_STATE(ServerState, ERROR, ev); + } + } + + void START(ServerEvent ev) { + handle = run_ffmpeg(config); + if(handle == nullptr) { + state(ServerState::ERROR); + } else { + state(ServerState::READING); + } + } + + void READING(ServerEvent ev) { res = fgets(buffer, BUF_MAX, handle); - print("OUT: {}", res); - } while(res != nullptr); - int rc = pclose(handle); - dbc::check(rc == 0, "error closing ffmpeg"); + if(res == nullptr) { + state(ServerState::STOP); + } else { + state(ServerState::READING); + } + } + + void STOP(ServerEvent ev) { + int rc = pclose(handle); + if(rc != 0) { + state(ServerState::ERROR); + } else { + state(ServerState::STOP); + } + } + + void ERROR(ServerEvent ev) { + state(ServerState::ERROR); + } +}; + +int main() { + Config config("config.json"); + Server server(config); + + while(!server.stopped()) { + server.event(ServerEvent::GO); + } }