From a12f91da633552be233eab1a1807f59d2f5b2df3 Mon Sep 17 00:00:00 2001 From: sladro Date: Sat, 3 Jan 2026 16:12:03 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=8A=9F=E8=83=BDbug?= =?UTF-8?q?=EF=BC=8C=E5=AF=B9=E9=BD=90prd?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build.yml | 5 ++- include/frame/frame.h | 2 +- include/utils/logger.h | 47 ++++++++++++++++++++ include/utils/spsc_queue.h | 36 ++++++++++------ plugins/alarm/actions/snapshot_action.cpp | 52 +++++++++++++++++++---- plugins/input_file/input_file_node.cpp | 14 +++--- plugins/input_rtsp/input_rtsp_node.cpp | 14 +++--- plugins/publish/publish_node.cpp | 4 +- src/ai_scheduler.cpp | 20 +++++---- src/graph_manager.cpp | 26 ++++++++++++ src/http_server.cpp | 40 ++++++++++++++--- src/media_server_app.cpp | 23 +++++----- 12 files changed, 218 insertions(+), 65 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d8bd00a..3ecbf9d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,9 +2,10 @@ name: CI on: push: - branches: [ main ] + branches: [ master, main ] pull_request: - branches: [ main ] + branches: [ master, main ] + workflow_dispatch: jobs: host-build: diff --git a/include/frame/frame.h b/include/frame/frame.h index 0ff562a..76b832b 100644 --- a/include/frame/frame.h +++ b/include/frame/frame.h @@ -54,7 +54,7 @@ struct Frame { int stride = 0; int dma_fd = -1; - uint64_t pts = 0; + uint64_t pts = 0; // microseconds uint64_t frame_id = 0; uint8_t* data = nullptr; // base pointer if mapped to CPU diff --git a/include/utils/logger.h b/include/utils/logger.h index 790d227..858e7fa 100644 --- a/include/utils/logger.h +++ b/include/utils/logger.h @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -14,6 +15,37 @@ namespace rk3588 { enum class LogLevel { Debug, Info, Warn, Error }; +inline const char* LogLevelToString(LogLevel lvl) { + switch (lvl) { + case LogLevel::Debug: return "debug"; + case LogLevel::Info: return "info"; + case LogLevel::Warn: return "warn"; + case LogLevel::Error: return "error"; + } + return "info"; +} + +inline bool ParseLogLevel(std::string s, LogLevel& out) { + for (char& c : s) c = static_cast(std::tolower(static_cast(c))); + if (s == "debug" || s == "d") { + out = LogLevel::Debug; + return true; + } + if (s == "info" || s == "i") { + out = LogLevel::Info; + return true; + } + if (s == "warn" || s == "warning" || s == "w") { + out = LogLevel::Warn; + return true; + } + if (s == "error" || s == "e") { + out = LogLevel::Error; + return true; + } + return false; +} + class Logger { public: static Logger& Instance() { @@ -27,7 +59,21 @@ public: TrimLocked(); } + void SetLevel(LogLevel lvl) { + std::lock_guard lock(mu_); + min_level_ = lvl; + } + + LogLevel GetLevel() const { + std::lock_guard lock(mu_); + return min_level_; + } + void Log(LogLevel lvl, const std::string& msg) { + { + std::lock_guard lock(mu_); + if (static_cast(lvl) < static_cast(min_level_)) return; + } const std::string line = FormatLine(lvl, msg); { std::lock_guard lock(mu_); @@ -89,6 +135,7 @@ private: mutable std::mutex mu_; std::deque lines_; size_t max_lines_ = 2000; + LogLevel min_level_ = LogLevel::Info; }; inline void LogDebug(const std::string& msg) { Logger::Instance().Log(LogLevel::Debug, msg); } diff --git a/include/utils/spsc_queue.h b/include/utils/spsc_queue.h index a03e0f5..7ea6637 100644 --- a/include/utils/spsc_queue.h +++ b/include/utils/spsc_queue.h @@ -3,8 +3,8 @@ #include #include #include -#include #include +#include #include namespace rk3588 { @@ -28,14 +28,18 @@ public: }; SpscQueue(size_t capacity, QueueDropStrategy strategy) - : capacity_(capacity), strategy_(strategy) {} + : capacity_(capacity == 0 ? 1 : capacity), strategy_(strategy) { + buf_.resize(capacity_); + } bool Push(T item) { std::unique_lock lock(mu_); if (stop_) return false; - if (queue_.size() >= capacity_) { + if (size_ >= capacity_) { if (strategy_ == QueueDropStrategy::DropOldest) { - queue_.pop_front(); + // Drop the oldest item. + head_ = (head_ + 1) % capacity_; + --size_; ++dropped_; } else if (strategy_ == QueueDropStrategy::DropNewest) { // Drop the incoming item. @@ -43,11 +47,13 @@ public: return true; } else { // Block until space is available - space_cv_.wait(lock, [&] { return queue_.size() < capacity_ || stop_; }); + space_cv_.wait(lock, [&] { return size_ < capacity_ || stop_; }); if (stop_) return false; } } - queue_.push_back(std::move(item)); + buf_[tail_] = std::move(item); + tail_ = (tail_ + 1) % capacity_; + ++size_; ++pushed_; data_cv_.notify_one(); return true; @@ -55,12 +61,13 @@ public: bool Pop(T& out, std::chrono::milliseconds timeout) { std::unique_lock lock(mu_); - if (!data_cv_.wait_for(lock, timeout, [&] { return !queue_.empty() || stop_; })) { + if (!data_cv_.wait_for(lock, timeout, [&] { return size_ > 0 || stop_; })) { return false; } - if (queue_.empty()) return false; - out = std::move(queue_.front()); - queue_.pop_front(); + if (size_ == 0) return false; + out = std::move(buf_[head_]); + head_ = (head_ + 1) % capacity_; + --size_; ++popped_; space_cv_.notify_one(); return true; @@ -80,7 +87,7 @@ public: size_t Size() const { std::lock_guard lock(mu_); - return queue_.size(); + return size_; } size_t Capacity() const { return capacity_; } @@ -103,7 +110,7 @@ public: Stats GetStats() const { std::lock_guard lock(mu_); Stats s; - s.size = queue_.size(); + s.size = size_; s.capacity = capacity_; s.dropped = dropped_; s.pushed = pushed_; @@ -118,7 +125,10 @@ private: mutable std::mutex mu_; std::condition_variable data_cv_; std::condition_variable space_cv_; - std::deque queue_; + std::vector buf_; + size_t head_ = 0; + size_t tail_ = 0; + size_t size_ = 0; bool stop_ = false; size_t dropped_ = 0; size_t pushed_ = 0; diff --git a/plugins/alarm/actions/snapshot_action.cpp b/plugins/alarm/actions/snapshot_action.cpp index ea0a2a7..7c88000 100644 --- a/plugins/alarm/actions/snapshot_action.cpp +++ b/plugins/alarm/actions/snapshot_action.cpp @@ -148,19 +148,55 @@ std::vector SnapshotAction::EncodeJpeg(const std::shared_ptr& fr uint8_t* src_data[4] = {nullptr}; int src_linesize[4] = {0}; + auto plane_ptr = [&](int idx) -> uint8_t* { + if (idx < 0 || idx >= frame->plane_count) return nullptr; + if (frame->planes[idx].data) return frame->planes[idx].data; + if (!frame->data) return nullptr; + const int off = frame->planes[idx].offset; + if (off < 0 || static_cast(off) >= frame->data_size) return nullptr; + return frame->data + off; + }; + auto plane_stride = [&](int idx, int fallback) -> int { + if (idx >= 0 && idx < frame->plane_count && frame->planes[idx].stride > 0) { + return frame->planes[idx].stride; + } + if (frame->stride > 0) return frame->stride; + return fallback; + }; + auto validate_bounds = [&](uint8_t* ptr, int stride, int h) -> bool { + if (!frame->data || frame->data_size == 0) return true; + if (!ptr || stride <= 0 || h <= 0) return false; + const uint8_t* base = frame->data; + const uint8_t* end = base + frame->data_size; + if (ptr < base || ptr >= end) return true; // Unknown ownership; cannot validate. + const size_t off = static_cast(ptr - base); + const size_t need = off + static_cast(stride) * static_cast(h); + return need <= frame->data_size; + }; + if (frame->format == PixelFormat::RGB || frame->format == PixelFormat::BGR) { - src_data[0] = frame->data; - src_linesize[0] = frame->width * 3; + src_data[0] = plane_ptr(0) ? plane_ptr(0) : frame->data; + src_linesize[0] = plane_stride(0, frame->width * 3); + if (!validate_bounds(src_data[0], src_linesize[0], frame->height)) { + sws_freeContext(sws); + av_frame_free(&av_frame); + avcodec_free_context(&ctx); + std::cerr << "[SnapshotAction] invalid RGB buffer size/stride (data_size=" << frame->data_size + << ", stride=" << src_linesize[0] << ", h=" << frame->height << ")\n"; + return output; + } } else if (frame->format == PixelFormat::NV12) { - src_data[0] = frame->planes[0].data ? frame->planes[0].data : frame->data; - src_data[1] = frame->planes[1].data ? frame->planes[1].data - : frame->data + frame->width * frame->height; + src_data[0] = plane_ptr(0) ? plane_ptr(0) : frame->data; + src_data[1] = plane_ptr(1); + if (!src_data[1] && frame->data) { + src_data[1] = frame->data + static_cast(frame->width) * frame->height; + } src_linesize[0] = frame->planes[0].stride > 0 ? frame->planes[0].stride : frame->width; src_linesize[1] = frame->planes[1].stride > 0 ? frame->planes[1].stride : frame->width; } else if (frame->format == PixelFormat::YUV420) { - src_data[0] = frame->planes[0].data ? frame->planes[0].data : frame->data; - src_data[1] = frame->planes[1].data; - src_data[2] = frame->planes[2].data; + src_data[0] = plane_ptr(0) ? plane_ptr(0) : frame->data; + src_data[1] = plane_ptr(1); + src_data[2] = plane_ptr(2); src_linesize[0] = frame->planes[0].stride > 0 ? frame->planes[0].stride : frame->width; src_linesize[1] = frame->planes[1].stride > 0 ? frame->planes[1].stride : frame->width / 2; src_linesize[2] = frame->planes[2].stride > 0 ? frame->planes[2].stride : frame->width / 2; diff --git a/plugins/input_file/input_file_node.cpp b/plugins/input_file/input_file_node.cpp index 8e422e4..23775c2 100644 --- a/plugins/input_file/input_file_node.cpp +++ b/plugins/input_file/input_file_node.cpp @@ -160,7 +160,7 @@ private: static_cast(y_size)}; frame->frame_id = ++frame_id_; - frame->pts = duration_cast(steady_clock::now().time_since_epoch()).count(); + frame->pts = duration_cast(steady_clock::now().time_since_epoch()).count(); PushToDownstream(frame); if (realtime_ && interval.count() > 0) { @@ -263,7 +263,7 @@ private: const int64_t best_pts = f.pts; frame->pts = best_pts == AV_NOPTS_VALUE ? 0 - : static_cast(av_rescale_q(best_pts, time_base, {1, 1000})); + : static_cast(av_rescale_q(best_pts, time_base, {1, 1000000})); if (fmt == PixelFormat::NV12 && plane_count == 2) { int y_size = width * height; @@ -396,7 +396,7 @@ private: } } - bool Decode(const uint8_t* data, size_t size, bool eos, int64_t pts_ms, + bool Decode(const uint8_t* data, size_t size, bool eos, int64_t pts_us, const std::function& on_frame) { if (!ctx || !mpi || !data || size == 0) return false; @@ -408,7 +408,7 @@ private: mpp_packet_set_size(packet, size); mpp_packet_set_pos(packet, pkt_data); mpp_packet_set_length(packet, size); - mpp_packet_set_pts(packet, pts_ms); + mpp_packet_set_pts(packet, pts_us); if (eos) mpp_packet_set_eos(packet); bool pkt_done = false; @@ -586,9 +586,9 @@ private: av_packet_unref(pkt); continue; } - int64_t pts_ms = pkt->pts == AV_NOPTS_VALUE ? 0 - : av_rescale_q(pkt->pts, time_base, {1, 1000}); - dec.Decode(pkt->data, pkt->size, false, pts_ms, + int64_t pts_us = pkt->pts == AV_NOPTS_VALUE ? 0 + : av_rescale_q(pkt->pts, time_base, {1, 1000000}); + dec.Decode(pkt->data, pkt->size, false, pts_us, [&](MppFrame frm) { PushFrameFromMpp(frm); if (realtime_ && interval.count() > 0) { diff --git a/plugins/input_rtsp/input_rtsp_node.cpp b/plugins/input_rtsp/input_rtsp_node.cpp index fcf2045..b3391e8 100644 --- a/plugins/input_rtsp/input_rtsp_node.cpp +++ b/plugins/input_rtsp/input_rtsp_node.cpp @@ -130,7 +130,7 @@ private: frame->planes[0] = {nullptr, width_, width_ * height_, 0}; frame->planes[1] = {nullptr, width_, width_ * height_ / 2, width_ * height_}; frame->frame_id = ++frame_id_; - frame->pts = duration_cast(steady_clock::now().time_since_epoch()).count(); + frame->pts = duration_cast(steady_clock::now().time_since_epoch()).count(); PushToDownstream(frame); if (frame_id_ % 100 == 0) { @@ -275,7 +275,7 @@ private: frame->frame_id = ++frame_id_; auto best_pts = f.pts; // av_frame_get_best_effort_timestamp is deprecated frame->pts = best_pts == AV_NOPTS_VALUE ? 0 - : static_cast(av_rescale_q(best_pts, time_base, {1, 1000})); + : static_cast(av_rescale_q(best_pts, time_base, {1, 1000000})); frame->data_owner = buffer; if (fmt == PixelFormat::NV12 && plane_count == 2) { @@ -345,7 +345,7 @@ private: } } - bool Decode(const uint8_t* data, size_t size, bool eos, int64_t pts_ms, + bool Decode(const uint8_t* data, size_t size, bool eos, int64_t pts_us, const std::function& on_frame) { if (!ctx || !mpi || !data || size == 0) return false; @@ -357,7 +357,7 @@ private: mpp_packet_set_size(packet, size); mpp_packet_set_pos(packet, pkt_data); mpp_packet_set_length(packet, size); - mpp_packet_set_pts(packet, pts_ms); + mpp_packet_set_pts(packet, pts_us); if (eos) mpp_packet_set_eos(packet); bool pkt_done = false; @@ -504,9 +504,9 @@ private: av_packet_unref(pkt); continue; } - int64_t pts_ms = pkt->pts == AV_NOPTS_VALUE ? 0 - : av_rescale_q(pkt->pts, time_base, {1, 1000}); - dec.Decode(pkt->data, pkt->size, false, pts_ms, + int64_t pts_us = pkt->pts == AV_NOPTS_VALUE ? 0 + : av_rescale_q(pkt->pts, time_base, {1, 1000000}); + dec.Decode(pkt->data, pkt->size, false, pts_us, [&](MppFrame frm) { PushFrameFromMpp(frm); }); av_packet_unref(pkt); } diff --git a/plugins/publish/publish_node.cpp b/plugins/publish/publish_node.cpp index 3909bd2..367d73f 100644 --- a/plugins/publish/publish_node.cpp +++ b/plugins/publish/publish_node.cpp @@ -518,8 +518,8 @@ public: } } int64_t mpp_pts = mpp_packet_get_pts(packet); - // Use encoder's pts if valid, otherwise compute from frame count - out.pts_ms = mpp_pts > 0 ? mpp_pts : static_cast(frame_count_ * 1000 / fps_); + // Frame::pts is microseconds; ZLMediaKit/FFmpeg muxer paths use millisecond timestamps. + out.pts_ms = mpp_pts > 0 ? (mpp_pts / 1000) : static_cast(frame_count_ * 1000 / fps_); ++frame_count_; if (on_packet) on_packet(out); mpp_packet_deinit(&packet); diff --git a/src/ai_scheduler.cpp b/src/ai_scheduler.cpp index e20adba..89615e0 100644 --- a/src/ai_scheduler.cpp +++ b/src/ai_scheduler.cpp @@ -2,7 +2,8 @@ #include #include -#include + +#include "utils/logger.h" namespace rk3588 { @@ -12,7 +13,7 @@ AiScheduler& AiScheduler::Instance() { } AiScheduler::AiScheduler() { - std::cout << "[AiScheduler] initialized\n"; + LogInfo("[AiScheduler] initialized"); } AiScheduler::~AiScheduler() { @@ -25,8 +26,8 @@ void AiScheduler::Shutdown() { std::lock_guard lock(models_mutex_); models_.clear(); } - std::cout << "[AiScheduler] shutdown, total inferences: " << total_inferences_.load() - << ", errors: " << total_errors_.load() << "\n"; + LogInfo("[AiScheduler] shutdown, total inferences: " + std::to_string(total_inferences_.load()) + + ", errors: " + std::to_string(total_errors_.load())); #endif } @@ -104,10 +105,11 @@ ModelHandle AiScheduler::LoadModel(const std::string& model_path, std::string& e models_[handle] = ctx; } - std::cout << "[AiScheduler] loaded model: " << model_path - << " (handle=" << handle << ", input=" << ctx->input_w - << "x" << ctx->input_h << "x" << ctx->input_c - << ", outputs=" << ctx->n_output << ")\n"; + LogInfo("[AiScheduler] loaded model: " + model_path + + " (handle=" + std::to_string(handle) + + ", input=" + std::to_string(ctx->input_w) + "x" + std::to_string(ctx->input_h) + + "x" + std::to_string(ctx->input_c) + + ", outputs=" + std::to_string(ctx->n_output) + ")"); return handle; #else @@ -129,7 +131,7 @@ void AiScheduler::UnloadModel(ModelHandle handle) { } } if (erased) { - std::cout << "[AiScheduler] unloaded model handle=" << handle << "\n"; + LogInfo("[AiScheduler] unloaded model handle=" + std::to_string(handle)); } #else (void)handle; diff --git a/src/graph_manager.cpp b/src/graph_manager.cpp index 308549d..20412b5 100644 --- a/src/graph_manager.cpp +++ b/src/graph_manager.cpp @@ -806,6 +806,14 @@ bool GraphManager::Build(const SimpleJson& root_cfg, std::string& err) { if (!plugin_path.empty()) { loader_.SetPluginDir(plugin_path); } + + const std::string log_level = g->ValueOr("log_level", ""); + if (!log_level.empty()) { + LogLevel lvl; + if (ParseLogLevel(log_level, lvl)) { + Logger::Instance().SetLevel(lvl); + } + } } std::lock_guard lock(graphs_mu_); @@ -928,6 +936,17 @@ bool GraphManager::ReloadFromFile(const std::string& path, std::string& err) { return ""; }(); + std::optional new_log_level; + if (const SimpleJson* g = expanded.Find("global")) { + const std::string log_level = g->ValueOr("log_level", ""); + if (!log_level.empty()) { + LogLevel lvl; + if (ParseLogLevel(log_level, lvl)) { + new_log_level = lvl; + } + } + } + std::lock_guard lock(graphs_mu_); const SimpleJson prev_last_good = last_good_root_; @@ -1047,6 +1066,9 @@ bool GraphManager::ReloadFromFile(const std::string& path, std::string& err) { last_good_root_ = expanded; default_queue_size_ = new_default_queue_size; default_strategy_ = new_default_strategy; + if (new_log_level) { + Logger::Instance().SetLevel(*new_log_level); + } return true; } @@ -1149,6 +1171,10 @@ bool GraphManager::ReloadFromFile(const std::string& path, std::string& err) { default_queue_size_ = new_default_queue_size; default_strategy_ = new_default_strategy; + if (new_log_level) { + Logger::Instance().SetLevel(*new_log_level); + } + if (!last_good_path_.empty()) { std::string werr; if (!WriteTextFileAtomic(last_good_path_, StringifySimpleJson(last_good_root_), werr)) { diff --git a/src/http_server.cpp b/src/http_server.cpp index 710312c..4b25286 100644 --- a/src/http_server.cpp +++ b/src/http_server.cpp @@ -396,14 +396,14 @@ void HttpServer::Stop() { void HttpServer::ServerLoop() { if (!InitSockets()) { - std::cerr << "[HttpServer] socket init failed\n"; + LogError("[HttpServer] socket init failed"); running_.store(false); return; } Sock srv = socket(AF_INET, SOCK_STREAM, 0); if (srv == kInvalidSock) { - std::cerr << "[HttpServer] socket() failed\n"; + LogError("[HttpServer] socket() failed"); CleanupSockets(); running_.store(false); return; @@ -421,14 +421,14 @@ void HttpServer::ServerLoop() { addr.sin_addr.s_addr = htonl(INADDR_ANY); addr.sin_port = htons(static_cast(port_)); if (bind(srv, reinterpret_cast(&addr), sizeof(addr)) != 0) { - std::cerr << "[HttpServer] bind failed on port " << port_ << "\n"; + LogError("[HttpServer] bind failed on port " + std::to_string(port_)); CloseSock(srv); CleanupSockets(); running_.store(false); return; } if (listen(srv, 16) != 0) { - std::cerr << "[HttpServer] listen failed\n"; + LogError("[HttpServer] listen failed"); CloseSock(srv); CleanupSockets(); running_.store(false); @@ -436,7 +436,7 @@ void HttpServer::ServerLoop() { } (void)SetNonBlocking(srv); listen_sock_.store(static_cast(srv)); - std::cout << "[HttpServer] listening on 0.0.0.0:" << port_ << " (web_root=" << web_root_ << ")\n"; + LogInfo("[HttpServer] listening on 0.0.0.0:" + std::to_string(port_) + " (web_root=" + web_root_ + ")"); while (running_.load()) { fd_set rfds; @@ -527,6 +527,9 @@ void HttpServer::ServerLoop() { } oss << "]}"; resp.body = oss.str(); + } else if (req.path == "/api/log/level") { + const LogLevel lvl = Logger::Instance().GetLevel(); + resp.body = std::string("{\"level\":\"") + LogLevelToString(lvl) + "\"}"; } else if (req.path.rfind("/api/nodes/", 0) == 0) { // /api/nodes/{id}/metrics const std::string prefix = "/api/nodes/"; @@ -577,6 +580,33 @@ void HttpServer::ServerLoop() { } else { resp.body = OkJson(); } + } else if (req.path == "/api/log/level") { + if (req.body.empty()) { + resp.status = 400; + resp.body = ErrorJson("empty body"); + } else { + SimpleJson body; + std::string jerr; + if (!ParseSimpleJson(req.body, body, jerr)) { + resp.status = 400; + resp.body = ErrorJson(jerr); + } else { + const std::string level = body.ValueOr("level", ""); + if (level.empty()) { + resp.status = 400; + resp.body = ErrorJson("missing 'level'"); + } else { + LogLevel lvl; + if (!ParseLogLevel(level, lvl)) { + resp.status = 400; + resp.body = ErrorJson("invalid level; use debug|info|warn|error"); + } else { + Logger::Instance().SetLevel(lvl); + resp.body = OkJson(); + } + } + } + } } else if (req.path.rfind("/api/nodes/", 0) == 0) { // /api/nodes/{id}/config const std::string prefix = "/api/nodes/"; diff --git a/src/media_server_app.cpp b/src/media_server_app.cpp index 23eafdd..e137b4a 100644 --- a/src/media_server_app.cpp +++ b/src/media_server_app.cpp @@ -23,6 +23,7 @@ namespace fs = std::filesystem; #include "graph_manager.h" #include "http_server.h" +#include "utils/logger.h" #include "utils/simple_json.h" namespace rk3588 { @@ -53,7 +54,7 @@ std::string ResolvePluginDir() { void HandleSignal(int) { if (g_manager) { - std::cerr << "\n[MediaServerApp] Caught signal, stopping...\n"; + LogWarn("[MediaServerApp] Caught signal, stopping..."); g_manager->RequestStop(); } } @@ -124,9 +125,9 @@ void InotifyWatchLoop(const std::string& path, std::atomic& stop_flag, Gra if (matched) { std::string err; if (!mgr.ReloadFromFile(path, err)) { - std::cerr << "[MediaServerApp] hot reload failed: " << err << "\n"; + LogWarn("[MediaServerApp] hot reload failed: " + err); } else { - std::cout << "[MediaServerApp] hot reload succeeded\n"; + LogInfo("[MediaServerApp] hot reload succeeded"); } // Debounce burst events. std::this_thread::sleep_for(std::chrono::milliseconds(300)); @@ -158,9 +159,9 @@ void PollWatchLoop(const std::string& path, std::atomic& stop_flag, GraphM std::string err; if (!mgr.ReloadFromFile(path, err)) { - std::cerr << "[MediaServerApp] hot reload failed: " << err << "\n"; + LogWarn("[MediaServerApp] hot reload failed: " + err); } else { - std::cout << "[MediaServerApp] hot reload succeeded\n"; + LogInfo("[MediaServerApp] hot reload succeeded"); } #else (void)path; @@ -178,7 +179,7 @@ int MediaServerApp::Start() { SimpleJson root_cfg; std::string err; if (!GraphManager::LoadConfigFile(config_path_, root_cfg, err)) { - std::cerr << "[MediaServerApp] Failed to load config: " << err << "\n"; + LogError("[MediaServerApp] Failed to load config: " + err); return 1; } @@ -190,7 +191,7 @@ int MediaServerApp::Start() { } if (!graph_manager_.BuildFromFile(config_path_, err)) { - std::cerr << "[MediaServerApp] Failed to build graphs: " << err << "\n"; + LogError("[MediaServerApp] Failed to build graphs: " + err); return 1; } @@ -199,14 +200,14 @@ int MediaServerApp::Start() { signal(SIGTERM, HandleSignal); if (!graph_manager_.StartAll()) { - std::cerr << "[MediaServerApp] Failed to start graphs\n"; + LogError("[MediaServerApp] Failed to start graphs"); return 1; } HttpServer http(graph_manager_, metrics_port, web_root); (void)http.Start(); - std::cout << "[MediaServerApp] Running. Press Ctrl+C to stop.\n"; + LogInfo("[MediaServerApp] Running. Press Ctrl+C to stop."); std::atomic stop_watch{false}; std::thread watcher; @@ -220,7 +221,7 @@ int MediaServerApp::Start() { #else watcher = std::thread([&] { PollWatchLoop(config_path_, stop_watch, graph_manager_); }); #endif - std::cout << "[MediaServerApp] Hot reload enabled\n"; + LogInfo("[MediaServerApp] Hot reload enabled"); } graph_manager_.BlockUntilStop(); @@ -229,7 +230,7 @@ int MediaServerApp::Start() { stop_watch.store(true); if (watcher.joinable()) watcher.join(); - std::cout << "[MediaServerApp] Shutdown complete.\n"; + LogInfo("[MediaServerApp] Shutdown complete."); return 0; }