修bug
This commit is contained in:
parent
3e2a8e9d17
commit
359d523dbb
@ -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++;
|
||||
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user