From 359d523dbb2e9e64d7435440e3dfcedc01199a9b Mon Sep 17 00:00:00 2001 From: sladro Date: Mon, 5 Jan 2026 17:23:29 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AEbug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plugins/alarm/actions/clip_action.cpp | 214 ++++++++++++------ plugins/alarm/actions/snapshot_action.cpp | 250 ++++++++++++++-------- plugins/alarm/alarm_node.cpp | 148 ++++++++++++- 3 files changed, 454 insertions(+), 158 deletions(-) diff --git a/plugins/alarm/actions/clip_action.cpp b/plugins/alarm/actions/clip_action.cpp index 6381e0f..b80db3e 100644 --- a/plugins/alarm/actions/clip_action.cpp +++ b/plugins/alarm/actions/clip_action.cpp @@ -2,6 +2,8 @@ #include #include +#include +#include #include #include #include @@ -16,7 +18,6 @@ extern "C" { #include #include #include -#include } #define HAS_FFMPEG 1 #else @@ -25,6 +26,155 @@ extern "C" { namespace rk3588 { +namespace { + +inline uint8_t ClipU8(int v) { + if (v < 0) return 0; + if (v > 255) return 255; + return static_cast(v); +} + +inline const uint8_t* PlanePtr(const Frame& f, int idx) { + if (idx < 0 || idx >= f.plane_count) return nullptr; + if (f.planes[idx].data) return f.planes[idx].data; + if (!f.data) return nullptr; + const int off = f.planes[idx].offset; + if (off < 0) return nullptr; + return f.data + static_cast(off); +} + +inline int PlaneStride(const Frame& f, int idx, int fallback) { + if (idx >= 0 && idx < f.plane_count && f.planes[idx].stride > 0) return f.planes[idx].stride; + if (f.stride > 0) return f.stride; + return fallback; +} + +bool FillYuv420pFromFrame(const Frame& src, int width, int height, AVFrame* dst) { + if (!dst) return false; + if (dst->format != AV_PIX_FMT_YUV420P) return false; + if (width <= 0 || height <= 0) return false; + + uint8_t* y = dst->data[0]; + uint8_t* u = dst->data[1]; + uint8_t* v = dst->data[2]; + const int y_stride = dst->linesize[0]; + const int u_stride = dst->linesize[1]; + const int v_stride = dst->linesize[2]; + if (!y || !u || !v || y_stride <= 0 || u_stride <= 0 || v_stride <= 0) return false; + + if (src.width != width || src.height != height) return false; + + if (src.format == PixelFormat::YUV420) { + const uint8_t* sy = PlanePtr(src, 0); + const uint8_t* su = PlanePtr(src, 1); + const uint8_t* sv = PlanePtr(src, 2); + if (!sy || !su || !sv) return false; + const int sy_stride = PlaneStride(src, 0, width); + const int su_stride = PlaneStride(src, 1, width / 2); + const int sv_stride = PlaneStride(src, 2, width / 2); + + for (int row = 0; row < height; ++row) { + std::memcpy(y + row * y_stride, sy + row * sy_stride, static_cast(width)); + } + const int uv_h = height / 2; + const int uv_w = width / 2; + for (int row = 0; row < uv_h; ++row) { + std::memcpy(u + row * u_stride, su + row * su_stride, static_cast(uv_w)); + std::memcpy(v + row * v_stride, sv + row * sv_stride, static_cast(uv_w)); + } + return true; + } + + if (src.format == PixelFormat::NV12) { + const uint8_t* sy = PlanePtr(src, 0); + const uint8_t* suv = PlanePtr(src, 1); + if (!sy) return false; + const int sy_stride = PlaneStride(src, 0, width); + const int suv_stride = PlaneStride(src, 1, width); + if (!suv) { + if (!src.data) return false; + suv = src.data + static_cast(sy_stride) * static_cast(height); + } + + for (int row = 0; row < height; ++row) { + std::memcpy(y + row * y_stride, sy + row * sy_stride, static_cast(width)); + } + const int uv_h = height / 2; + const int uv_w = width / 2; + for (int row = 0; row < uv_h; ++row) { + const uint8_t* src_uv = suv + row * suv_stride; + uint8_t* dst_u = u + row * u_stride; + uint8_t* dst_v = v + row * v_stride; + for (int col = 0; col < uv_w; ++col) { + dst_u[col] = src_uv[col * 2 + 0]; + dst_v[col] = src_uv[col * 2 + 1]; + } + } + return true; + } + + if (src.format == PixelFormat::RGB || src.format == PixelFormat::BGR) { + const bool is_bgr = (src.format == PixelFormat::BGR); + const uint8_t* s = PlanePtr(src, 0); + if (!s) s = src.data; + if (!s) return false; + const int s_stride = PlaneStride(src, 0, width * 3); + + for (int row = 0; row < height; row += 2) { + const uint8_t* row0 = s + row * s_stride; + const uint8_t* row1 = (row + 1 < height) ? (s + (row + 1) * s_stride) : row0; + uint8_t* y0 = y + row * y_stride; + uint8_t* y1 = (row + 1 < height) ? (y + (row + 1) * y_stride) : y0; + uint8_t* uu = u + (row / 2) * u_stride; + uint8_t* vv = v + (row / 2) * v_stride; + + for (int col = 0; col < width; col += 2) { + int u_sum = 0; + int v_sum = 0; + int samples = 0; + + auto sample = [&](const uint8_t* p, uint8_t* ydst) { + const int b = is_bgr ? p[0] : p[2]; + const int g = p[1]; + const int r = is_bgr ? p[2] : p[0]; + const int yy = (77 * r + 150 * g + 29 * b + 128) >> 8; + *ydst = ClipU8(yy); + u_sum += (-43 * r - 84 * g + 127 * b); + v_sum += (127 * r - 106 * g - 21 * b); + samples += 1; + }; + + const uint8_t* p00 = row0 + col * 3; + const uint8_t* p01 = (col + 1 < width) ? (row0 + (col + 1) * 3) : p00; + const uint8_t* p10 = row1 + col * 3; + const uint8_t* p11 = (col + 1 < width) ? (row1 + (col + 1) * 3) : p10; + + sample(p00, &y0[col]); + if (col + 1 < width) sample(p01, &y0[col + 1]); + if (row + 1 < height) { + sample(p10, &y1[col]); + if (col + 1 < width) sample(p11, &y1[col + 1]); + } else { + sample(p10, &y1[col]); + if (col + 1 < width) sample(p11, &y1[col + 1]); + } + + const int denom = samples > 0 ? samples : 1; + const int u_val = ((u_sum / denom) + 128 * 256 + 128) >> 8; + const int v_val = ((v_sum / denom) + 128 * 256 + 128) >> 8; + const int uv_col = col / 2; + uu[uv_col] = ClipU8(u_val); + vv[uv_col] = ClipU8(v_val); + } + } + return true; + } + + return false; +} + +} // namespace + bool ClipAction::Init(const SimpleJson& config) { pre_sec_ = config.ValueOr("pre_sec", 5); post_sec_ = config.ValueOr("post_sec", 10); @@ -180,67 +330,7 @@ std::string ClipAction::ProcessClip(const std::vector>& f auto encode_frame = [&](const std::shared_ptr& frame) { if (!frame) return; - AVPixelFormat src_fmt = AV_PIX_FMT_NONE; - if (frame->format == PixelFormat::RGB) { - src_fmt = AV_PIX_FMT_RGB24; - } else if (frame->format == PixelFormat::BGR) { - src_fmt = AV_PIX_FMT_BGR24; - } else if (frame->format == PixelFormat::NV12) { - src_fmt = AV_PIX_FMT_NV12; - } else if (frame->format == PixelFormat::YUV420) { - src_fmt = AV_PIX_FMT_YUV420P; - } - if (src_fmt == AV_PIX_FMT_NONE) return; - - SwsContext* sws = sws_getContext(width, height, src_fmt, - width, height, AV_PIX_FMT_YUV420P, - SWS_BILINEAR, nullptr, nullptr, nullptr); - if (sws) { - uint8_t* src_data[4] = {nullptr, nullptr, nullptr, nullptr}; - int src_linesize[4] = {0, 0, 0, 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; - }; - - if (frame->format == PixelFormat::RGB || frame->format == PixelFormat::BGR) { - src_data[0] = plane_ptr(0) ? plane_ptr(0) : frame->data; - src_linesize[0] = plane_stride(0, width * 3); - } else if (frame->format == PixelFormat::NV12) { - 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(width) * height; - src_linesize[0] = plane_stride(0, width); - src_linesize[1] = plane_stride(1, width); - } else if (frame->format == PixelFormat::YUV420) { - 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] = plane_stride(0, width); - src_linesize[1] = plane_stride(1, width / 2); - src_linesize[2] = plane_stride(2, width / 2); - } - - if (!src_data[0]) { - sws_freeContext(sws); - return; - } - sws_scale(sws, src_data, src_linesize, 0, height, - av_frame->data, av_frame->linesize); - sws_freeContext(sws); - } + if (!FillYuv420pFromFrame(*frame, width, height, av_frame)) return; av_frame->pts = pts++; diff --git a/plugins/alarm/actions/snapshot_action.cpp b/plugins/alarm/actions/snapshot_action.cpp index 7c88000..17d20ed 100644 --- a/plugins/alarm/actions/snapshot_action.cpp +++ b/plugins/alarm/actions/snapshot_action.cpp @@ -1,6 +1,8 @@ #include "snapshot_action.h" #include +#include +#include #include #include #include @@ -10,7 +12,7 @@ extern "C" { #include #include -#include +#include } #define HAS_FFMPEG 1 #else @@ -19,6 +21,159 @@ extern "C" { namespace rk3588 { +namespace { + +inline uint8_t ClipU8(int v) { + if (v < 0) return 0; + if (v > 255) return 255; + return static_cast(v); +} + +inline const uint8_t* PlanePtr(const Frame& f, int idx) { + if (idx < 0 || idx >= f.plane_count) return nullptr; + if (f.planes[idx].data) return f.planes[idx].data; + if (!f.data) return nullptr; + const int off = f.planes[idx].offset; + if (off < 0) return nullptr; + return f.data + static_cast(off); +} + +inline int PlaneStride(const Frame& f, int idx, int fallback) { + if (idx >= 0 && idx < f.plane_count && f.planes[idx].stride > 0) return f.planes[idx].stride; + if (f.stride > 0) return f.stride; + return fallback; +} + +bool FillYuv420pFromFrame(const Frame& src, AVFrame* dst) { + if (!dst) return false; + if (dst->format != AV_PIX_FMT_YUV420P) return false; + if (src.width <= 0 || src.height <= 0) return false; + + const int w = src.width; + const int h = src.height; + uint8_t* y = dst->data[0]; + uint8_t* u = dst->data[1]; + uint8_t* v = dst->data[2]; + const int y_stride = dst->linesize[0]; + const int u_stride = dst->linesize[1]; + const int v_stride = dst->linesize[2]; + if (!y || !u || !v || y_stride <= 0 || u_stride <= 0 || v_stride <= 0) return false; + + if (src.format == PixelFormat::YUV420) { + const uint8_t* sy = PlanePtr(src, 0); + const uint8_t* su = PlanePtr(src, 1); + const uint8_t* sv = PlanePtr(src, 2); + if (!sy || !su || !sv) return false; + const int sy_stride = PlaneStride(src, 0, w); + const int su_stride = PlaneStride(src, 1, w / 2); + const int sv_stride = PlaneStride(src, 2, w / 2); + + for (int row = 0; row < h; ++row) { + std::memcpy(y + row * y_stride, sy + row * sy_stride, static_cast(w)); + } + const int uv_h = h / 2; + const int uv_w = w / 2; + for (int row = 0; row < uv_h; ++row) { + std::memcpy(u + row * u_stride, su + row * su_stride, static_cast(uv_w)); + std::memcpy(v + row * v_stride, sv + row * sv_stride, static_cast(uv_w)); + } + return true; + } + + if (src.format == PixelFormat::NV12) { + const uint8_t* sy = PlanePtr(src, 0); + const uint8_t* suv = PlanePtr(src, 1); + if (!sy) return false; + + const int sy_stride = PlaneStride(src, 0, w); + const int suv_stride = PlaneStride(src, 1, w); + if (!suv) { + // Fallback: packed NV12 layout + if (!src.data) return false; + suv = src.data + static_cast(sy_stride) * static_cast(h); + } + + for (int row = 0; row < h; ++row) { + std::memcpy(y + row * y_stride, sy + row * sy_stride, static_cast(w)); + } + + const int uv_h = h / 2; + const int uv_w = w / 2; + for (int row = 0; row < uv_h; ++row) { + const uint8_t* src_uv = suv + row * suv_stride; + uint8_t* dst_u = u + row * u_stride; + uint8_t* dst_v = v + row * v_stride; + for (int col = 0; col < uv_w; ++col) { + dst_u[col] = src_uv[col * 2 + 0]; + dst_v[col] = src_uv[col * 2 + 1]; + } + } + return true; + } + + if (src.format == PixelFormat::RGB || src.format == PixelFormat::BGR) { + const bool is_bgr = (src.format == PixelFormat::BGR); + const uint8_t* s = PlanePtr(src, 0); + if (!s) s = src.data; + if (!s) return false; + const int s_stride = PlaneStride(src, 0, w * 3); + + for (int row = 0; row < h; row += 2) { + const uint8_t* row0 = s + row * s_stride; + const uint8_t* row1 = (row + 1 < h) ? (s + (row + 1) * s_stride) : row0; + uint8_t* y0 = y + row * y_stride; + uint8_t* y1 = (row + 1 < h) ? (y + (row + 1) * y_stride) : y0; + uint8_t* uu = u + (row / 2) * u_stride; + uint8_t* vv = v + (row / 2) * v_stride; + + for (int col = 0; col < w; col += 2) { + int u_sum = 0; + int v_sum = 0; + int samples = 0; + + auto sample = [&](const uint8_t* p, uint8_t* ydst) { + const int b = is_bgr ? p[0] : p[2]; + const int g = p[1]; + const int r = is_bgr ? p[2] : p[0]; + const int yy = (77 * r + 150 * g + 29 * b + 128) >> 8; + *ydst = ClipU8(yy); + u_sum += (-43 * r - 84 * g + 127 * b); + v_sum += (127 * r - 106 * g - 21 * b); + samples += 1; + }; + + const uint8_t* p00 = row0 + col * 3; + const uint8_t* p01 = (col + 1 < w) ? (row0 + (col + 1) * 3) : p00; + const uint8_t* p10 = row1 + col * 3; + const uint8_t* p11 = (col + 1 < w) ? (row1 + (col + 1) * 3) : p10; + + sample(p00, &y0[col]); + if (col + 1 < w) sample(p01, &y0[col + 1]); + if (row + 1 < h) { + sample(p10, &y1[col]); + if (col + 1 < w) sample(p11, &y1[col + 1]); + } else { + // If no second row, count row0 twice for chroma. + sample(p10, &y1[col]); + if (col + 1 < w) sample(p11, &y1[col + 1]); + } + + const int denom = samples > 0 ? samples : 1; + const int u_val = ((u_sum / denom) + 128 * 256 + 128) >> 8; + const int v_val = ((v_sum / denom) + 128 * 256 + 128) >> 8; + const int uv_col = col / 2; + uu[uv_col] = ClipU8(u_val); + vv[uv_col] = ClipU8(v_val); + } + } + return true; + } + + return false; +} + +} // namespace + bool SnapshotAction::Init(const SimpleJson& config) { format_ = config.ValueOr("format", "jpg"); quality_ = config.ValueOr("quality", 85); @@ -92,7 +247,8 @@ std::vector SnapshotAction::EncodeJpeg(const std::shared_ptr& fr ctx->width = frame->width; ctx->height = frame->height; ctx->time_base = AVRational{1, 25}; - ctx->pix_fmt = AV_PIX_FMT_YUVJ420P; + ctx->pix_fmt = AV_PIX_FMT_YUV420P; + ctx->color_range = AVCOL_RANGE_JPEG; // Set quality (1-31, lower is better for MJPEG) int q = 31 - (quality_ * 30 / 100); @@ -107,7 +263,8 @@ std::vector SnapshotAction::EncodeJpeg(const std::shared_ptr& fr AVFrame* av_frame = av_frame_alloc(); av_frame->width = frame->width; av_frame->height = frame->height; - av_frame->format = AV_PIX_FMT_YUVJ420P; + av_frame->format = AV_PIX_FMT_YUV420P; + av_frame->color_range = AVCOL_RANGE_JPEG; if (av_frame_get_buffer(av_frame, 32) < 0) { av_frame_free(&av_frame); @@ -115,97 +272,12 @@ std::vector SnapshotAction::EncodeJpeg(const std::shared_ptr& fr return output; } - // Convert input frame to YUV420P - AVPixelFormat src_fmt = AV_PIX_FMT_NONE; - if (frame->format == PixelFormat::RGB) { - src_fmt = AV_PIX_FMT_RGB24; - } else if (frame->format == PixelFormat::BGR) { - src_fmt = AV_PIX_FMT_BGR24; - } else if (frame->format == PixelFormat::NV12) { - src_fmt = AV_PIX_FMT_NV12; - } else if (frame->format == PixelFormat::YUV420) { - src_fmt = AV_PIX_FMT_YUV420P; - } - - if (src_fmt == AV_PIX_FMT_NONE) { + if (!FillYuv420pFromFrame(*frame, av_frame)) { av_frame_free(&av_frame); avcodec_free_context(&ctx); return output; } - SwsContext* sws = sws_getContext( - frame->width, frame->height, src_fmt, - frame->width, frame->height, AV_PIX_FMT_YUVJ420P, - SWS_BILINEAR, nullptr, nullptr, nullptr); - - if (!sws) { - av_frame_free(&av_frame); - avcodec_free_context(&ctx); - return output; - } - - // Set up source pointers - 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] = 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] = 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] = 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; - } - - sws_scale(sws, src_data, src_linesize, 0, frame->height, - av_frame->data, av_frame->linesize); - sws_freeContext(sws); - av_frame->pts = 0; AVPacket* pkt = av_packet_alloc(); diff --git a/plugins/alarm/alarm_node.cpp b/plugins/alarm/alarm_node.cpp index 4098630..01535c8 100644 --- a/plugins/alarm/alarm_node.cpp +++ b/plugins/alarm/alarm_node.cpp @@ -1,5 +1,7 @@ #include #include +#include +#include #include #include #include @@ -24,6 +26,128 @@ private: std::shared_ptr frame; }; + static std::shared_ptr CloneFrameForRingBuffer(const std::shared_ptr& src) { + if (!src) return nullptr; + if (src->width <= 0 || src->height <= 0) return nullptr; + if (!src->data) return nullptr; + + auto out = std::make_shared(); + out->width = src->width; + out->height = src->height; + out->format = src->format; + out->pts = src->pts; + out->frame_id = src->frame_id; + out->det = src->det; + out->user_meta = src->user_meta; + out->dma_fd = -1; + + const int w = src->width; + const int h = src->height; + + auto plane_ptr = [&](int idx) -> const uint8_t* { + if (idx < 0 || idx >= src->plane_count) return nullptr; + if (src->planes[idx].data) return src->planes[idx].data; + const int off = src->planes[idx].offset; + if (off < 0) return nullptr; + return src->data + static_cast(off); + }; + auto plane_stride = [&](int idx, int fallback) -> int { + if (idx >= 0 && idx < src->plane_count && src->planes[idx].stride > 0) return src->planes[idx].stride; + if (src->stride > 0) return src->stride; + return fallback; + }; + + if (src->format == PixelFormat::NV12) { + const uint8_t* sy = plane_ptr(0); + const uint8_t* suv = plane_ptr(1); + if (!sy) return nullptr; + const int sy_stride = plane_stride(0, w); + const int suv_stride = plane_stride(1, w); + if (!suv) { + suv = src->data + static_cast(sy_stride) * static_cast(h); + } + + const int y_stride = w; + const int uv_stride = w; + const size_t y_bytes = static_cast(y_stride) * static_cast(h); + const size_t uv_bytes = static_cast(uv_stride) * static_cast(h / 2); + auto buf = std::make_shared>(y_bytes + uv_bytes); + out->data_owner = buf; + out->data = buf->data(); + out->data_size = buf->size(); + out->stride = y_stride; + out->plane_count = 2; + out->planes[0] = {out->data, y_stride, static_cast(y_bytes), 0}; + out->planes[1] = {out->data + y_bytes, uv_stride, static_cast(uv_bytes), static_cast(y_bytes)}; + + // Copy Y + for (int row = 0; row < h; ++row) { + std::memcpy(out->planes[0].data + row * y_stride, sy + row * sy_stride, static_cast(w)); + } + // Copy UV (interleaved) + for (int row = 0; row < h / 2; ++row) { + std::memcpy(out->planes[1].data + row * uv_stride, suv + row * suv_stride, static_cast(w)); + } + return out; + } + + if (src->format == PixelFormat::YUV420) { + const uint8_t* sy = plane_ptr(0); + const uint8_t* su = plane_ptr(1); + const uint8_t* sv = plane_ptr(2); + if (!sy || !su || !sv) return nullptr; + const int sy_stride = plane_stride(0, w); + const int su_stride = plane_stride(1, w / 2); + const int sv_stride = plane_stride(2, w / 2); + + const int y_stride = w; + const int uv_stride = w / 2; + const size_t y_bytes = static_cast(y_stride) * static_cast(h); + const size_t u_bytes = static_cast(uv_stride) * static_cast(h / 2); + const size_t v_bytes = u_bytes; + auto buf = std::make_shared>(y_bytes + u_bytes + v_bytes); + out->data_owner = buf; + out->data = buf->data(); + out->data_size = buf->size(); + out->stride = y_stride; + out->plane_count = 3; + out->planes[0] = {out->data, y_stride, static_cast(y_bytes), 0}; + out->planes[1] = {out->data + y_bytes, uv_stride, static_cast(u_bytes), static_cast(y_bytes)}; + out->planes[2] = {out->data + y_bytes + u_bytes, uv_stride, static_cast(v_bytes), static_cast(y_bytes + u_bytes)}; + + for (int row = 0; row < h; ++row) { + std::memcpy(out->planes[0].data + row * y_stride, sy + row * sy_stride, static_cast(w)); + } + for (int row = 0; row < h / 2; ++row) { + std::memcpy(out->planes[1].data + row * uv_stride, su + row * su_stride, static_cast(w / 2)); + std::memcpy(out->planes[2].data + row * uv_stride, sv + row * sv_stride, static_cast(w / 2)); + } + return out; + } + + if (src->format == PixelFormat::RGB || src->format == PixelFormat::BGR) { + const uint8_t* s = plane_ptr(0); + if (!s) s = src->data; + if (!s) return nullptr; + const int s_stride = plane_stride(0, w * 3); + const int dst_stride = w * 3; + const size_t bytes = static_cast(dst_stride) * static_cast(h); + auto buf = std::make_shared>(bytes); + out->data_owner = buf; + out->data = buf->data(); + out->data_size = buf->size(); + out->stride = dst_stride; + out->plane_count = 1; + out->planes[0] = {out->data, dst_stride, static_cast(bytes), 0}; + for (int row = 0; row < h; ++row) { + std::memcpy(out->data + row * dst_stride, s + row * s_stride, static_cast(dst_stride)); + } + return out; + } + + return nullptr; + } + public: std::string Id() const override { return id_; } std::string Type() const override { return "alarm"; } @@ -289,17 +413,27 @@ public: NodeStatus Process(FramePtr frame) override { if (!frame) return NodeStatus::DROP; - std::lock_guard lock(mu_); - if (frame_buffer_) { + // Copy pointer out of lock; FrameRingBuffer is internally synchronized. + std::shared_ptr fb; + { + std::lock_guard lock(mu_); + fb = frame_buffer_; + } + if (fb) { using namespace std::chrono; const uint64_t ts_ms = static_cast(duration_cast(steady_clock::now().time_since_epoch()).count()); - frame_buffer_->Push(frame, ts_ms); + // IMPORTANT: store a CPU copy so the ring buffer doesn't extend DMA-BUF lifetime. + if (auto copied = CloneFrameForRingBuffer(frame)) { + fb->Push(std::move(copied), ts_ms); + } } - auto result = rule_engine_.Evaluate(frame); - if (result.matched) TriggerAlarm(result, frame); - - ++processed_frames_; + { + std::lock_guard lock(mu_); + auto result = rule_engine_.Evaluate(frame); + if (result.matched) TriggerAlarm(result, frame); + ++processed_frames_; + } return NodeStatus::OK; }