// rtsp_manager.hpp #pragma once #include #include #include #include #include #include #include #include #include #include "app_config.hpp" #include "mqtt_client_wrapper.hpp" class RTMPManager { public: enum class StreamResult { OK, PIPELINE_ERROR, CONNECTION_FAIL, EOS_RECEIVED, UNKNOWN }; struct StreamStatus { bool running; StreamResult last_result; std::string last_error; }; using StreamCallback = std::function; static void init(); static void start_camera(const Camera &cam, StreamType type, const std::string &seqNo); static void stop_camera(const std::string &cam_name, StreamType type, const std::string &seqNo); static void stop_all(); static bool is_streaming(const std::string &cam_name, StreamType type); static bool is_any_streaming(); static std::string get_stream_url(const std::string &cam_name, StreamType type); static void set_status_callback(StreamCallback cb); private: struct StreamContext { std::atomic running{false}; std::thread thread; StreamStatus status; StreamContext() = default; StreamContext(const StreamContext &) = delete; StreamContext &operator=(const StreamContext &) = delete; }; static std::unordered_map> streams; static std::mutex streams_mutex; static StreamCallback status_callback; static void stream_loop(Camera cam, StreamType type, const std::string &seqNo); static GstElement *create_pipeline(const Camera &cam, StreamType type); static std::string make_stream_key(const std::string &cam_name, StreamType type); static void update_status(const std::string &key, const StreamStatus &status); static void publish_stream_status(const Camera &cam, StreamType type, const std::string &seqNo, bool ok, const std::string &reason); static inline std::string stream_type_suffix(StreamType type) { return (type == StreamType::MAIN) ? "_main" : "_sub"; } };