#include #include #include #include #include #include "hw/i_image_processor.h" #include "node.h" #include "utils/logger.h" namespace rk3588 { namespace { PixelFormat ParseFormat(const std::string& s) { if (s == "nv12" || s == "NV12") return PixelFormat::NV12; if (s == "yuv420" || s == "YUV420") return PixelFormat::YUV420; if (s == "rgb" || s == "RGB") return PixelFormat::RGB; if (s == "bgr" || s == "BGR") return PixelFormat::BGR; return PixelFormat::UNKNOWN; } } // namespace class PreprocessNode : public INode { public: std::string Id() const override { return id_; } std::string Type() const override { return "preprocess"; } bool Init(const SimpleJson& config, const NodeContext& ctx) override { id_ = config.ValueOr("id", "preprocess"); dst_w_ = config.ValueOr("dst_w", 640); dst_h_ = config.ValueOr("dst_h", 640); keep_ratio_ = config.ValueOr("keep_ratio", false); std::string fmt_str = config.ValueOr("dst_format", ""); if (!fmt_str.empty()) { dst_fmt_ = ParseFormat(fmt_str); } const bool requested_use_rga = config.ValueOr("use_rga", true); use_rga_ = requested_use_rga; if (const SimpleJson* dbg = config.Find("debug"); dbg && dbg->IsObject()) { stats_log_ = dbg->ValueOr("stats", stats_log_); stats_interval_ = std::max( 1, static_cast(dbg->ValueOr("stats_interval", static_cast(stats_interval_)))); } input_queue_ = ctx.input_queue; if (!input_queue_) { LogError("[preprocess] no input queue for node " + id_); return false; } if (ctx.output_queues.empty()) { LogError("[preprocess] no output queue for node " + id_); return false; } output_queues_ = ctx.output_queues; #if !defined(RK3588_ENABLE_RGA) if (requested_use_rga) { LogError("[preprocess] use_rga=true but RGA not enabled at build time"); return false; } use_rga_ = false; #endif #if !defined(RK3588_ENABLE_FFMPEG) if (!use_rga_) { LogError("[preprocess] neither RGA nor FFmpeg enabled"); return false; } #endif image_processor_ = ctx.image_processor; if (!image_processor_) { LogError("[preprocess] no image processor for node " + id_); return false; } return true; } bool Start() override { LogInfo("[preprocess] start id=" + id_ + " dst=" + std::to_string(dst_w_) + "x" + std::to_string(dst_h_) + (use_rga_ ? " (rga)" : " (swscale)")); return true; } void Stop() override {} NodeStatus Process(FramePtr frame) override { if (!frame) return NodeStatus::DROP; if (!image_processor_) return NodeStatus::ERROR; PixelFormat out_fmt = (dst_fmt_ != PixelFormat::UNKNOWN) ? dst_fmt_ : frame->format; int out_w = dst_w_; int out_h = dst_h_; if (out_w <= 0) out_w = frame->width; if (out_h <= 0) out_h = frame->height; if (keep_ratio_ && dst_w_ > 0 && dst_h_ > 0 && frame->width > 0 && frame->height > 0) { float scale = std::min(static_cast(dst_w_) / frame->width, static_cast(dst_h_) / frame->height); out_w = static_cast(frame->width * scale); out_h = static_cast(frame->height * scale); out_w = (out_w + 1) & ~1; out_h = (out_h + 1) & ~1; } const bool need_resize = (frame->width != out_w || frame->height != out_h); const bool need_cvt = (frame->format != out_fmt); if (need_resize) { WarnMetaResizeOnce(frame, out_w, out_h); } if (!need_resize && !need_cvt) { ProcessPassthrough(frame); return NodeStatus::OK; } Frame out; out.width = out_w; out.height = out_h; out.format = out_fmt; Status st = image_processor_->Resize(*frame, out); if (st.Failed()) { if (!use_rga_ && st.ErrMessage().find("unsupported format") != std::string::npos) { ProcessPassthrough(frame); return NodeStatus::OK; } LogError("[preprocess] " + st.ErrMessage()); return NodeStatus::ERROR; } auto out_frame = std::make_shared(out); out_frame->pts = frame->pts; out_frame->frame_id = frame->frame_id; out_frame->det = frame->det; out_frame->face_det = frame->face_det; out_frame->face_recog = frame->face_recog; out_frame->user_meta = frame->user_meta; PushToDownstream(out_frame); ++processed_; if (stats_log_ && stats_interval_ > 0 && (processed_ % stats_interval_) == 0) { LogInfo("[preprocess] " + std::string(use_rga_ ? "rga" : "swscale") + " frame=" + std::to_string(out_frame->frame_id) + " " + std::to_string(frame->width) + "x" + std::to_string(frame->height) + " -> " + std::to_string(out_w) + "x" + std::to_string(out_h) + " id=" + id_); } return NodeStatus::OK; } private: void PushToDownstream(FramePtr frame) { for (auto& q : output_queues_) { q->Push(frame); } } void WarnMetaResizeOnce(const FramePtr& frame, int out_w, int out_h) { if (warned_meta_resize_) return; if (!frame) return; if (frame->width == out_w && frame->height == out_h) return; if (!frame->det && !frame->face_det && !frame->face_recog) return; warned_meta_resize_ = true; LogWarn("[preprocess] resized frame but forwarded det/face meta without coordinate scaling; ensure det/recog/osd use same resolution (id=" + id_ + ")"); } void ProcessPassthrough(FramePtr frame) { PushToDownstream(frame); ++processed_; if (stats_log_ && stats_interval_ > 0 && (processed_ % stats_interval_) == 0) { LogInfo("[preprocess] passthrough frame=" + std::to_string(frame->frame_id) + " id=" + id_); } } std::string id_; int dst_w_ = 640; int dst_h_ = 640; bool keep_ratio_ = false; PixelFormat dst_fmt_ = PixelFormat::UNKNOWN; bool use_rga_ = true; bool stats_log_ = false; uint64_t stats_interval_ = 100; bool warned_meta_resize_ = false; std::shared_ptr> input_queue_; std::vector>> output_queues_; std::shared_ptr image_processor_; uint64_t processed_ = 0; }; REGISTER_NODE(PreprocessNode, "preprocess"); } // namespace rk3588