修bug
Some checks are pending
CI / host-build (push) Waiting to run
CI / rk3588-cross-build (push) Waiting to run

This commit is contained in:
sladro 2026-01-05 17:23:29 +08:00
parent 3e2a8e9d17
commit 359d523dbb
3 changed files with 454 additions and 158 deletions

View File

@ -2,6 +2,8 @@
#include <algorithm>
#include <chrono>
#include <cstdint>
#include <cstring>
#include <ctime>
#include <filesystem>
#include <fstream>
@ -16,7 +18,6 @@ extern "C" {
#include <libavformat/avformat.h>
#include <libavutil/imgutils.h>
#include <libavutil/opt.h>
#include <libswscale/swscale.h>
}
#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<uint8_t>(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<size_t>(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<size_t>(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<size_t>(uv_w));
std::memcpy(v + row * v_stride, sv + row * sv_stride, static_cast<size_t>(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<size_t>(sy_stride) * static_cast<size_t>(height);
}
for (int row = 0; row < height; ++row) {
std::memcpy(y + row * y_stride, sy + row * sy_stride, static_cast<size_t>(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<int>("pre_sec", 5);
post_sec_ = config.ValueOr<int>("post_sec", 10);
@ -180,67 +330,7 @@ std::string ClipAction::ProcessClip(const std::vector<std::shared_ptr<Frame>>& f
auto encode_frame = [&](const std::shared_ptr<Frame>& 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<size_t>(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<size_t>(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++;

View File

@ -1,6 +1,8 @@
#include "snapshot_action.h"
#include <chrono>
#include <cstdint>
#include <cstring>
#include <ctime>
#include <iomanip>
#include <iostream>
@ -10,7 +12,7 @@
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
#include <libavutil/pixfmt.h>
}
#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<uint8_t>(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<size_t>(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<size_t>(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<size_t>(uv_w));
std::memcpy(v + row * v_stride, sv + row * sv_stride, static_cast<size_t>(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<size_t>(sy_stride) * static_cast<size_t>(h);
}
for (int row = 0; row < h; ++row) {
std::memcpy(y + row * y_stride, sy + row * sy_stride, static_cast<size_t>(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<std::string>("format", "jpg");
quality_ = config.ValueOr<int>("quality", 85);
@ -92,7 +247,8 @@ std::vector<uint8_t> SnapshotAction::EncodeJpeg(const std::shared_ptr<Frame>& 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<uint8_t> SnapshotAction::EncodeJpeg(const std::shared_ptr<Frame>& 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<uint8_t> SnapshotAction::EncodeJpeg(const std::shared_ptr<Frame>& 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<size_t>(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<size_t>(ptr - base);
const size_t need = off + static_cast<size_t>(stride) * static_cast<size_t>(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<size_t>(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();

View File

@ -1,5 +1,7 @@
#include <atomic>
#include <chrono>
#include <cstdint>
#include <cstring>
#include <iostream>
#include <memory>
#include <mutex>
@ -24,6 +26,128 @@ private:
std::shared_ptr<Frame> frame;
};
static std::shared_ptr<Frame> CloneFrameForRingBuffer(const std::shared_ptr<Frame>& src) {
if (!src) return nullptr;
if (src->width <= 0 || src->height <= 0) return nullptr;
if (!src->data) return nullptr;
auto out = std::make_shared<Frame>();
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<size_t>(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<size_t>(sy_stride) * static_cast<size_t>(h);
}
const int y_stride = w;
const int uv_stride = w;
const size_t y_bytes = static_cast<size_t>(y_stride) * static_cast<size_t>(h);
const size_t uv_bytes = static_cast<size_t>(uv_stride) * static_cast<size_t>(h / 2);
auto buf = std::make_shared<std::vector<uint8_t>>(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<int>(y_bytes), 0};
out->planes[1] = {out->data + y_bytes, uv_stride, static_cast<int>(uv_bytes), static_cast<int>(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<size_t>(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<size_t>(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<size_t>(y_stride) * static_cast<size_t>(h);
const size_t u_bytes = static_cast<size_t>(uv_stride) * static_cast<size_t>(h / 2);
const size_t v_bytes = u_bytes;
auto buf = std::make_shared<std::vector<uint8_t>>(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<int>(y_bytes), 0};
out->planes[1] = {out->data + y_bytes, uv_stride, static_cast<int>(u_bytes), static_cast<int>(y_bytes)};
out->planes[2] = {out->data + y_bytes + u_bytes, uv_stride, static_cast<int>(v_bytes), static_cast<int>(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<size_t>(w));
}
for (int row = 0; row < h / 2; ++row) {
std::memcpy(out->planes[1].data + row * uv_stride, su + row * su_stride, static_cast<size_t>(w / 2));
std::memcpy(out->planes[2].data + row * uv_stride, sv + row * sv_stride, static_cast<size_t>(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<size_t>(dst_stride) * static_cast<size_t>(h);
auto buf = std::make_shared<std::vector<uint8_t>>(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<int>(bytes), 0};
for (int row = 0; row < h; ++row) {
std::memcpy(out->data + row * dst_stride, s + row * s_stride, static_cast<size_t>(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<std::mutex> lock(mu_);
if (frame_buffer_) {
// Copy pointer out of lock; FrameRingBuffer is internally synchronized.
std::shared_ptr<FrameRingBuffer> fb;
{
std::lock_guard<std::mutex> lock(mu_);
fb = frame_buffer_;
}
if (fb) {
using namespace std::chrono;
const uint64_t ts_ms = static_cast<uint64_t>(duration_cast<milliseconds>(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<std::mutex> lock(mu_);
auto result = rule_engine_.Evaluate(frame);
if (result.matched) TriggerAlarm(result, frame);
++processed_frames_;
}
return NodeStatus::OK;
}