You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

865 lines
28 KiB

7 months ago
// cam_ws_app.c : Headless V4L2 → WebSocket 스트리머
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <signal.h>
#include <errno.h>
#include <pthread.h>
#include <linux/videodev2.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <time.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <turbojpeg.h>
#include <arm_neon.h>
#include <omp.h>
#include "uds_frame.h"
#include "uds_server.h"
#include "uds_det.h"
#include "ctrl_flags.h"
#include "v4l2_interface.h"
#include "nc_utils.h"
#include "nc_ts_fsync_flipflop_buffers.h"
#include "nc_app_config_parser.h"
#include "nc_cnn_aiware_runtime.h"
#include "nc_cnn_communicator.h"
#include "nc_cnn_worker_for_postprocess.h"
#include "nc_neon.h"
#ifdef AIWARE_DEVICE_SUPPORTED
#include "aiware/runtime/c/aiwaredevice.h"
#endif
#ifdef USE_8MP_VI
#include "nc_dsr_helper.h"
#include "nc_dsr_set.h"
#include "nc_dmabuf_ctrl_helper.h"
#endif
// -----------------------------
// 설정
// -----------------------------
#define VIS0_MAX_CH (0)
#define VIS1_MAX_CH (1)
#define VIDEO_MAX_CH (VIS0_MAX_CH + VIS1_MAX_CH)
#define VIDEO_BUFFER_NUM (3)
/* don't modify */
#define MAX_WIDTH_FOR_VDMA_CNN_DS (1920)
#define MAX_HEIGHT_FOR_VDMA_CNN_DS (1080)
#ifdef USE_8MP_VI
#define VIDEO_WIDTH MAX_WIDTH_FOR_VDMA_CNN_DS
#define VIDEO_HEIGHT MAX_HEIGHT_FOR_VDMA_CNN_DS
#else
#define VIDEO_WIDTH (1920)
#define VIDEO_HEIGHT (1080)
#endif
#define MQ_NAME_CNN_BUF "/cnn_data"
#define DEV_FILE_DSR "/dev/dsr"
#ifdef SHOW_FACE_DETECT
#define NETWORK_FILE_FACE_DET "/mnt/user_data/applications/misc/networks/cuuva/modified_yolov8n-face.aiwbin"
#endif
#ifdef SHOW_CUUVA_DETECT
#define NETWORK_FILE_CUUVA_DET "/mnt/user_data/applications/misc/networks/cuuva/yolov8s_coco_640x384_apache6sr250_aiw4939.aiwbin"
#endif
#ifdef SHOW_FIRE_DETECT
#define NETWORK_FILE_FIRE_DET "/mnt/user_data/applications/misc/networks/cuuva/modified_best_fire_detect.aiwbin"
#endif
#ifdef SHOW_LPR_DETECT
#define NETWORK_FILE_LPR_DET "/mnt/user_data/applications/misc/networks/cuuva/modified_best_lp_detect.aiwbin"
#endif
static volatile int g_running = 1;
st_nc_v4l2_config v4l2_config[VIDEO_MAX_CH];
st_npu_input_info npu_input_info;
#define FOLLOWUP_CLS_ID 0
#define NETWORK_OBJDET 0
#define NETWORK_ABNORM 1
#define NETWORK_FIRE 2
#define NETWORK_LPR 3
#define NETWORK_FACE 4
static uint32_t ch_seq[VIDEO_MAX_CH] = {0};
enum { SLOT_FREE=0, SLOT_WRITING=1, SLOT_READY=2 };
static inline void mem_fence(void) { __sync_synchronize(); }
int uds_server_start(const char* sock_path);
void uds_server_stop(const char* sock_path);
int uds_send_frame(int ch, const void* frame_ptr,
uint32_t w, uint32_t h, uint32_t stride,
uint32_t pixfmt, uint64_t ts_ns);
static inline uint64_t now_ns(void){
struct timespec ts; clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
return (uint64_t)ts.tv_sec*1000000000ull + ts.tv_nsec;
}
static inline uint64_t now_us(void){
struct timespec ts; clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
return (uint64_t)ts.tv_sec*1000000ull + ts.tv_nsec/1000ull;
}
static uint32_t fra_sec_cnt[VIDEO_MAX_CH] = {0};
static uint32_t fra_sec_cnt_total = 0;
static uint64_t fps_win_start_us = 0;
#ifdef USE_8MP_VI
size_t BUFF_SIZE;
int dsr_fd;
DSR_Data_t dsr_info;
dma_alloc_info dma_info;
st_nc_dsr_config dsr_config;
img_input_config dsr_input_config;
img_output_config dsr_output_config;
#endif
#ifdef USE_8MP_VI
static int dsr_init(void)
{
int input_fd = 0, output_fd = 0;
long page_size = sysconf(_SC_PAGESIZE);
dsr_input_config.format = IMG_FORMAT_RGB888;
dsr_input_config.width = MAX_WIDTH_FOR_VDMA_CNN_DS;
dsr_input_config.height = MAX_HEIGHT_FOR_VDMA_CNN_DS;
dsr_output_config.format = IMG_FORMAT_RGB888;
BUFF_SIZE = dsr_input_config.width * dsr_input_config.height * 3;
BUFF_SIZE = ((BUFF_SIZE + page_size - 1) / page_size) * page_size;
if(dsr_config_downscale(&dsr_config, 1, NPU_INPUT_WIDTH, NPU_INPUT_HEIGHT) < 0){
perror("Error: DSR config fail");
return -1;
}
if(open_device_and_dma_buffers(DEV_FILE_DSR, &dsr_fd, &input_fd, &output_fd, BUFF_SIZE) < 0){
perror("Error: DSR open fail");
return -1;
}
if(dsr_setup_buffer(&dsr_info, dsr_fd, &dma_info, input_fd, output_fd, BUFF_SIZE) < 0){
perror("Error: DSR setup fail");
return -1;
}
return 0;
}
static int dsr_deinit(void)
{
nc_dmabuf_ctrl_end_cpu_access(dma_info.dmabuf_fd_in);
nc_dmabuf_ctrl_end_cpu_access(dma_info.dmabuf_fd_out);
nc_dmabuf_ctrl_free_dma_fd(dma_info.dmabuf_fd_in);
nc_dmabuf_ctrl_free_dma_fd(dma_info.dmabuf_fd_out);
nc_dmabuf_ctrl_close();
for (int i = 0; i < BUFF_NUM; i++) {
if (dsr_info.dsr_in_buf[i] != MAP_FAILED) {
munmap(dsr_info.dsr_in_buf[i], BUFF_SIZE);
}
if (dsr_info.dsr_out_buf[i] != MAP_FAILED) {
munmap(dsr_info.dsr_out_buf[i], BUFF_SIZE);
}
}
dsr_device_deinit(&dsr_fd);
return 0;
}
#endif
// -----------------------------
// Ctrl+C 핸들러
// -----------------------------
static void on_sigint(int sig) {
(void)sig;
g_running = 0;
}
// -----------------------------
// 채널 설정
// -----------------------------
static void set_v4l2_config(void)
{
// VIS0 영역
for (int i = 0; i < VIS0_MAX_CH; i++) {
v4l2_config[i].video_buf.video_device_num = CNN_DEVICE_NUM(VISION0) + i;
v4l2_config[i].video_buf.video_fd = -1;
v4l2_config[i].dma_mode = INTERLEAVE;
v4l2_config[i].img_process = MODE_DS;
v4l2_config[i].pixformat = V4L2_PIX_FMT_RGB24;
v4l2_config[i].crop_x_start = 0;
v4l2_config[i].crop_y_start = 0;
v4l2_config[i].crop_width = 0;
v4l2_config[i].crop_height = 0;
v4l2_config[i].ds_width = VIDEO_WIDTH;
v4l2_config[i].ds_height = VIDEO_HEIGHT;
}
// VIS1 영역
for (int j = VIS0_MAX_CH; j < VIDEO_MAX_CH; j++) {
v4l2_config[j].video_buf.video_device_num = CNN_DEVICE_NUM(VISION1) + j;
v4l2_config[j].video_buf.video_fd = -1;
v4l2_config[j].dma_mode = INTERLEAVE;
v4l2_config[j].img_process = MODE_DS;
v4l2_config[j].pixformat = V4L2_PIX_FMT_RGB24;
v4l2_config[j].crop_x_start = 0;
v4l2_config[j].crop_y_start = 0;
v4l2_config[j].crop_width = 0;
v4l2_config[j].crop_height = 0;
v4l2_config[j].ds_width = VIDEO_WIDTH;
v4l2_config[j].ds_height = VIDEO_HEIGHT;
}
}
int send_cnn_buf (uint8_t *ptr_cnn_buf, uint64_t time_stamp_us, uint32_t cam_ch, E_NETWORK_UID net_id)
{
int ret = 0;
stCnnData *cnn_data;
struct mq_attr attr;
attr.mq_maxmsg = MAX_MQ_MSG_CNT;
attr.mq_msgsize = sizeof(stCnnData*);
int oflag = O_WRONLY | O_CREAT;
mqd_t mfd = mq_open(MQ_NAME_CNN_BUF, oflag, 0666, &attr);
if (mfd == -1) {
perror("mq open error");
return -1;
}
cnn_data = (stCnnData*)malloc(sizeof(stCnnData));
cnn_data->cam_ch = cam_ch;
cnn_data->ptr_cnn_buf = ptr_cnn_buf;
cnn_data->time_stamp_us = time_stamp_us;
cnn_data->net_id = net_id;
if ((ret = mq_send(mfd, (const char *)&cnn_data, attr.mq_msgsize, 1)) == -1) {
printf("errno of mq_send = %d\n", errno);
}
mq_close(mfd);
return ret;
}
int receive_cnn_buf (stCnnData **out_cnn_buf)
{
int ret = 0;
struct mq_attr attr;
attr.mq_maxmsg = MAX_MQ_MSG_CNT;
attr.mq_msgsize = sizeof(stCnnData *);
int oflag = O_RDONLY | O_CREAT;
mqd_t mfd = mq_open(MQ_NAME_CNN_BUF, oflag, 0666, &attr);
if (mfd == -1) {
perror("mq open error");
return -1;
}
if ((ret = (int32_t)mq_receive(mfd, (char*)out_cnn_buf, attr.mq_msgsize, NULL)) == -1) {
printf("errno of mq_receive = %d\n", errno);
}
mq_close(mfd);
return ret;
}
int npu_init(st_npu_input_info *npu_input_info)
{
/* Initialize CNN */
if (nc_aiw_init_cnn() < 0 ) {
fprintf(stderr, "nc_aiw_init_cnn() failure!!\n");
return -1;
}
#ifdef SHOW_FACE_DETECT
if (nc_aiw_add_network_to_builder(nc_localize_path((const char *)NETWORK_FILE_FACE_DET), NETWORK_FACE_DET, nc_postprocess_yolov8_inference_result) < 0) {
fprintf(stderr, "nc_aiw_add_network_to_builder() failure!!\n");
return -1;
}
#endif
#ifdef SHOW_CUUVA_DETECT
if (nc_aiw_add_network_to_builder(nc_localize_path((const char *)NETWORK_FILE_CUUVA_DET), NETWORK_CUUVA_DET, nc_postprocess_yolov8_inference_result) < 0) {
fprintf(stderr, "nc_aiw_add_network_to_builder() failure!!\n");
return -1;
}
#endif
#ifdef SHOW_FIRE_DETECT
if (nc_aiw_add_network_to_builder(nc_localize_path((const char *)NETWORK_FILE_FIRE_DET), NETWORK_FIRE_DET, nc_postprocess_yolov8_inference_result) < 0) {
fprintf(stderr, "nc_aiw_add_network_to_builder() failure!!\n");
return -1;
}
#endif
#ifdef SHOW_LPR_DETECT
if (nc_aiw_add_network_to_builder(nc_localize_path((const char *)NETWORK_FILE_LPR_DET), NETWORK_LPR_DET, nc_postprocess_yolov8_inference_result) < 0) {
fprintf(stderr, "nc_aiw_add_network_to_builder() failure!!\n");
return -1;
}
#endif
if(nc_aiw_finish_network_builder() < 0 ) {
fprintf(stderr, "nc_aiw_finish_network_builder() failure!!\n");
return -1;
}
// obtain the input resoltution for the CNN network
// get information of the input tensor
aiwTensorInfo in_tinfo;
#ifdef SHOW_FACE_DETECT
if(nc_get_cnn_network_input_resol(NETWORK_FACE_DET, &in_tinfo) < 0)
{
printf("failed to get the input resolution for the CNN network\n");
}
#endif
#ifdef SHOW_CUUVA_DETECT
if(nc_get_cnn_network_input_resol(NETWORK_CUUVA_DET, &in_tinfo) < 0)
{
printf("failed to get the input resolution for the CNN network\n");
}
#endif
#ifdef SHOW_FIRE_DETECT
if(nc_get_cnn_network_input_resol(NETWORK_FIRE_DET, &in_tinfo) < 0)
{
printf("failed to get the input resolution for the CNN network\n");
}
#endif
#ifdef SHOW_LPR_DETECT
if(nc_get_cnn_network_input_resol(NETWORK_LPR_DET, &in_tinfo) < 0)
{
printf("failed to get the input resolution for the CNN network\n");
}
#endif
npu_input_info->w = in_tinfo.dim.w;// network input width
npu_input_info->h = in_tinfo.dim.h;// network input height
npu_input_info->rgb_size = in_tinfo.dim.w * in_tinfo.dim.h * RGB_CNT;
printf("aiw finish\n");
return 0;
}
void *cnn_task(void *arg)
{
(void) arg;
printf("CNN TASK RUN!!\n");
while (g_running) {
stCnnData *cnn_data = NULL;
if(receive_cnn_buf(&cnn_data) != -1){
nc_aiw_run_cnn(cnn_data->ptr_cnn_buf, cnn_data->time_stamp_us, cnn_data->cam_ch, cnn_data->net_id);
if(cnn_data) {
free(cnn_data->ptr_cnn_buf);
free(cnn_data);
}
}
}
printf("EXIT CNN_TASK!!\n");
return NULL;
}
// 원본 버퍼 복제해서 MQ로 전송 (수신측 free)
static inline void dup_and_send(unsigned char* src, size_t sz,
uint64_t ts_us, uint32_t ch, E_NETWORK_UID net_uid){
unsigned char* p = (unsigned char*)malloc(sz);
if (!p) return;
memcpy(p, src, sz);
send_cnn_buf(p, ts_us, ch, net_uid);
}
static void dispatch_stage1_and_plan_followups(
uint32_t m,
uint32_t ch,
unsigned char* rgb_planar, size_t rgb_size,
uint64_t ts_us)
{
const int UID_DET = nc_get_cnn_networks_id_by_uid(NETWORK_CUUVA_DET);
const int UID_FIRE = nc_get_cnn_networks_id_by_uid(NETWORK_FIRE_DET);
if (CTRL_MODEL_ON(m, NETWORK_OBJDET) && UID_DET >= 0)
dup_and_send(rgb_planar, rgb_size, ts_us, ch, (E_NETWORK_UID)UID_DET);
if (CTRL_MODEL_ON(m, NETWORK_FIRE) && UID_FIRE >= 0)
dup_and_send(rgb_planar, rgb_size, ts_us, ch, (E_NETWORK_UID)UID_FIRE);
}
static void dispatch_stage2_followups(
uint32_t m, // ctrl_active_models_acquire() 결과(모델 비트)
uint32_t ch,
unsigned char* rgb_planar, size_t rgb_size,
uint64_t ts_us,
bool need_face,
bool need_lpr
){
const int UID_FACE = nc_get_cnn_networks_id_by_uid(NETWORK_FACE_DET);
const int UID_LPR = nc_get_cnn_networks_id_by_uid(NETWORK_LPR_DET);
// const int UID_FIRE = nc_get_cnn_networks_id_by_uid(NETWORK_FIRE_DET);
// const int UID_FIRE = nc_get_cnn_networks_id_by_uid(NETWORK_FIRE_DET);
if (need_face && CTRL_MODEL_ON(m, NETWORK_FACE) && UID_FACE >= 0){
dup_and_send(rgb_planar, rgb_size, ts_us, ch, (E_NETWORK_UID)UID_FACE);
}
if (need_lpr && CTRL_MODEL_ON(m, NETWORK_LPR) && UID_LPR >= 0){
dup_and_send(rgb_planar, rgb_size, ts_us, ch, (E_NETWORK_UID)UID_LPR);
}
// dup_and_send(rgb_planar, rgb_size, ts_us, ch, (E_NETWORK_UID)UID_FACE);
// if (need_abnorm && CTRL_MODEL_ON(m, NETWORK_ABNORM) && UID_ABNORM >= 0)
// dup_and_send(rgb_planar, rgb_size, ts_us, ch, (E_NETWORK_UID)UID_ABNORM);
}
// static inline void send_by_ctrl_mask(
// uint32_t m,
// uint32_t ch,
// unsigned char* rgb_planar, size_t rgb_size,
// uint64_t ts_us)
// {
// dispatch_stage1_and_plan_followups(m, ch, rgb_planar, rgb_size, ts_us);
// }
// -----------------------------
// V4L2 초기화
// -----------------------------
static int v4l2_initialize(void)
{
for (int i = 0; i < VIDEO_MAX_CH; i++) {
v4l2_config[i].video_buf.video_fd =
nc_v4l2_open(v4l2_config[i].video_buf.video_device_num, true);
if (v4l2_config[i].video_buf.video_fd == errno) {
fprintf(stderr, "[error] nc_v4l2_open() failure (ch=%d)\n", i);
return -1;
}
if (nc_v4l2_init_device_and_stream_on(&v4l2_config[i], VIDEO_BUFFER_NUM) < 0) {
fprintf(stderr, "[error] nc_v4l2_init_device_and_stream_on() failure (ch=%d)\n", i);
return -1;
}
}
nc_v4l2_show_user_config(&v4l2_config[0], VIDEO_MAX_CH);
return 0;
}
#ifndef ARRAY_LEN
#define ARRAY_LEN(a) ((uint32_t)(sizeof(a)/sizeof((a)[0])))
#endif
static void collect_and_append(
uint32_t ch,
const stCnnPostprocessingResults *R,
const char *tag,
bool *need_face_followup,
bool *need_lpr_followup,
uds_det_entry_t **total_items,
uint32_t *total_items_cnt
){
const uint32_t CLASS_CAP = ARRAY_LEN(R->class_objs);
uint32_t total = 0;
for (uint32_t ci = 0; ci < CLASS_CAP; ++ci) {
int cnt = R->class_objs[ci].obj_cnt;
if (cnt > 0) total += (uint32_t)cnt;
}
if (total == 0) return;
uds_det_entry_t *items = (uds_det_entry_t*)malloc((size_t)total * sizeof(uds_det_entry_t));
if (!items) return;
uint16_t tag_id = 0;
if (!strcmp(tag, "DET")) tag_id = UDS_TAG_DET;
else if (!strcmp(tag, "FIRE")) tag_id = UDS_TAG_FIRE;
else if (!strcmp(tag, "FACE")) tag_id = UDS_TAG_FACE;
else if (!strcmp(tag, "LPR")) tag_id = UDS_TAG_LPR;
uint32_t k = 0;
for (uint32_t ci = 0; ci < CLASS_CAP; ++ci) {
int n = R->class_objs[ci].obj_cnt;
if (n <= 0) continue;
const stObjInfo *objs = R->class_objs[ci].objs;
for (int j = 0; j < n; ++j) {
const stObjInfo *o = &objs[j];
uds_det_entry_t *dst = &items[k++];
dst->prob = (float)o->prob;
dst->x = (float)o->bbox.x;
dst->y = (float)o->bbox.y;
dst->w = (float)o->bbox.w;
dst->h = (float)o->bbox.h;
dst->cls = UDS_ENC_CLS(tag_id, (uint16_t)ci);
dst->reserved = UDS_RESERVED_PACK(ctrl_feat_mask_snapshot(), ctrl_active_models_acquire());
printf("[%s] ch=%u ci=%u j=%d k=%u prob=%.3f bbox=%.1f,%.1f,%.1f,%.1f\n",
tag, ch, ci, j, k, (float)o->prob,
(float)o->bbox.x, (float)o->bbox.y, (float)o->bbox.w, (float)o->bbox.h);
if ((uint32_t)ci == FOLLOWUP_CLS_ID && tag_id == UDS_TAG_DET) {
*need_face_followup = true;
}
if ((uint32_t)ci == FOLLOWUP_CLS_ID && tag_id == UDS_TAG_DET) {
*need_lpr_followup = true;
}
}
}
uds_det_entry_t *tmp = (uds_det_entry_t*)realloc(*total_items, (size_t)(*total_items_cnt + total) * sizeof(uds_det_entry_t));
if (tmp) {
*total_items = tmp;
memcpy(&(*total_items)[*total_items_cnt], items, (size_t)total * sizeof(uds_det_entry_t));
*total_items_cnt += total;
}
free(items);
}
static void read_and_collect_for(
uint32_t ch,
uint32_t network_offset,
const char *tag,
uint64_t *ts_ns_latest,
uds_det_entry_t **total_items,
uint32_t *total_items_cnt,
bool *need_face_followup,
bool *need_lpr_followup
){
uint64_t ts = 0;
pp_result_buf *det_buf = (pp_result_buf*)nc_tsfs_ff_get_readable_buffer_and_timestamp(ch + network_offset, &ts);
if (det_buf) {
stCnnPostprocessingResults *R = &det_buf->cnn_result;
collect_and_append(ch, R, tag, need_face_followup, need_lpr_followup, total_items, total_items_cnt);
uint64_t ts_ns = ts * 1000ULL;
if (ts_ns > *ts_ns_latest) *ts_ns_latest = ts_ns;
}
nc_tsfs_ff_finish_read_buf(ch + network_offset);
}
// static void send_face_followup_if_needed(
// bool need_face_followup,
// uint32_t ch,
// int *networkOrder,
// unsigned char *rgbdata_for_cnn,
// size_t rgb_size
// ){
// if (!need_face_followup) return;
// uint64_t ts_us = now_us();
// networkOrder[ch] = nc_get_cnn_networks_id_by_uid(NETWORK_YOLOV8_DET);
// unsigned char *rgbdata_for_face = (unsigned char*)malloc(rgb_size);
// if (!rgbdata_for_face) return;
// memcpy(rgbdata_for_face, rgbdata_for_cnn, rgb_size);
// send_cnn_buf(rgbdata_for_face, ts_us, (uint32_t)ch, (E_NETWORK_UID)networkOrder[ch]); // 수신측에서 free
// }
// -----------------------------
// 메인
// -----------------------------
int main(int argc, char** argv)
{
#ifdef USE_8MP_VI
puts("USE_8MP_VI defined (enabled)");
#else
puts("USE_8MP_VI not defined (disabled)");
#endif
(void)argc; (void)argv;
// SIGINT 핸들링
struct sigaction sa;
sa.sa_handler = on_sigint;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESETHAND;
sigaction(SIGINT, &sa, NULL);
pthread_t p_thread[MAX_TASK_CNT];
int task_cnt = 0;
int thr_id;
int status;
// 채널/디바이스 설정
memset(v4l2_config, 0, sizeof(v4l2_config));
set_v4l2_config();
ctrl_uds_start();
ctrl_feature_on(FEAT_OBJDET);
// V4L2 시작
if (v4l2_initialize() < 0) {
fprintf(stderr, "v4l2_initialize failed\n");
return -1;
}
if (uds_server_start(UDS_SOCK_PATH) != 0) {
fprintf(stderr, "uds_server_start failed\n"); return -1;
}
#ifdef USE_8MP_VI
dsr_init();
#endif
// create thread-safe flip-flop buffer
for (int i = 0; i < VIDEO_MAX_CH; i++) {
if (v4l2_config[i].video_buf.video_fd == -1){
}
else{
#ifdef DETECT_NETWORK
int det_buf_size = sizeof(pp_result_buf);
if(nc_tsfs_ff_create_buffers(i+DETECT_NETWORK, det_buf_size) < 0) {
exit(1);
}
#endif
#ifdef FACE_NETWORK
int face_buf_size = sizeof(pp_result_buf);
if(nc_tsfs_ff_create_buffers(i+FACE_NETWORK, face_buf_size) < 0) {
exit(1);
}
#endif
#ifdef FIRE_NETWORK
int fire_buf_size = sizeof(pp_result_buf);
if(nc_tsfs_ff_create_buffers(i+FIRE_NETWORK, fire_buf_size) < 0) {
exit(1);
}
#endif
#ifdef LPR_NETWORK
int lpr_buf_size = sizeof(pp_result_buf);
if(nc_tsfs_ff_create_buffers(i+LPR_NETWORK, lpr_buf_size) < 0) {
exit(1);
}
#endif
}
}
mq_unlink(MQ_NAME_CNN_BUF);
if(npu_init(&npu_input_info) < 0) {
printf("failed to init NPU\n");
return -1;
}
printf("create tasks\n");
thr_id = pthread_create(&p_thread[task_cnt++], NULL, cnn_task, (void *)NULL);
if (thr_id < 0) {
perror("thread create error : cnn_task");
exit(1);
}
cnn_postprocess_arg cnn_post_param;
cnn_post_param.target_width = VIDEO_WIDTH;
cnn_post_param.target_height = VIDEO_HEIGHT;
thr_id = pthread_create(&p_thread[task_cnt++], NULL, nc_cnn_postprocess_task, (void *)&cnn_post_param);
if (thr_id < 0) {
perror("thread create error : nc_cnn_postprocess_task");
exit(1);
}
unsigned char* rgbdata_for_cnn = (unsigned char *)malloc(npu_input_info.rgb_size);
bool need_face_followup = false;
bool need_lpr_followup = false;
while (g_running) {
// int networkOrder[VIDEO_MAX_CH];
uint32_t m = ctrl_active_models_acquire();
need_face_followup = false;
need_lpr_followup = false;
uint64_t time_stamp_us = 0;
for (int ch = 0; ch < VIDEO_MAX_CH; ch++) {
if (v4l2_config[ch].video_buf.video_fd < 0) continue;
struct v4l2_buffer video_buf;
CLEAR(video_buf);
if (nc_v4l2_dequeue_buffer(v4l2_config[ch].video_buf.video_fd, &video_buf) == -1) {
// non-fatal
continue;
}
// size_t bytes = FRAME_BYTES(w,h);
void* frame_ptr = v4l2_config[ch].video_buf.buffers[video_buf.index].start;
time_stamp_us = now_us();
// writer_push(H, ch, frame_ptr, bytes, time_stamp_us);
uint32_t m = ctrl_active_models_acquire();
// networkOrder[ch] = nc_get_cnn_networks_id_by_uid(NETWORK_FIRE_DET);
// unsigned char* rgbdata_for_det = (unsigned char *)malloc(npu_input_info.rgb_size);
#ifdef USE_8MP_VI
memcpy(dsr_info.dsr_in_buf[0], (uint8_t *)v4l2_config[ch].video_buf.buffers[video_buf.index].start, MAX_WIDTH_FOR_VDMA_CNN_DS*MAX_HEIGHT_FOR_VDMA_CNN_DS*RGB_CNT);
dsr_downscale(dsr_fd, &dsr_info, dsr_input_config, dsr_output_config, dsr_config, 0);
nc_rgb_interleaved_to_planar_neon((unsigned char *)dsr_info.dsr_out_buf[0] \
,(unsigned char *)rgbdata_for_cnn \
,(unsigned char *)rgbdata_for_cnn + NPU_INPUT_WIDTH*NPU_INPUT_HEIGHT \
,(unsigned char *)rgbdata_for_cnn + NPU_INPUT_WIDTH*NPU_INPUT_HEIGHT*2 \
, NPU_INPUT_WIDTH, NPU_INPUT_HEIGHT);
#else
memcpy(rgbdata_for_cnn, (unsigned char *)v4l2_config[ch].video_buf.buffers[video_buf.index].start, npu_input_info.rgb_size); // 600us
#endif
// memcpy(rgbdata_for_cnn, rgbdata_for_det, npu_input_info.rgb_size);
// (내부에서 k_feat2model 사용)
// printf("[SEND] ch=%d mask=0x%08X (CUUVA=%d YOLOV8=%d FIRE=%d)\n",
// ch, m,
// nc_get_cnn_networks_id_by_uid(NETWORK_CUUVA_DET),
// nc_get_cnn_networks_id_by_uid(NETWORK_YOLOV8_DET),
// nc_get_cnn_networks_id_by_uid(NETWORK_FIRE_DET));
uint64_t t0 = now_us();
uds_send_frame(ch, frame_ptr, VIDEO_WIDTH, VIDEO_HEIGHT, VIDEO_WIDTH * 3, WS_PIXFMT_RGB24, time_stamp_us);
printf("[SEND][FRA] took %llu us\n", (unsigned long long)(now_us() - t0));
dispatch_stage1_and_plan_followups(m, ch, rgbdata_for_cnn, npu_input_info.rgb_size, time_stamp_us);
fra_sec_cnt[ch]++;
fra_sec_cnt_total++;
if (fps_win_start_us == 0) fps_win_start_us = time_stamp_us;
if (nc_v4l2_queue_buffer(v4l2_config[ch].video_buf.video_fd, video_buf.index) == -1) {
fprintf(stderr, "Error VIDIOC_QBUF buffer %d (ch=%d)\n", video_buf.index, ch);
}
}
for (uint32_t ch = 0; ch < VIDEO_MAX_CH; ch++) {
if (v4l2_config[ch].video_buf.video_fd == -1) continue;
uint64_t ts_ns_latest = 0;
uds_det_entry_t *total_items = NULL;
uint32_t total_items_cnt = 0;
if (CTRL_MODEL_ON(m, NETWORK_OBJDET)){
read_and_collect_for(
ch, DETECT_NETWORK, "DET",
&ts_ns_latest, &total_items, &total_items_cnt, &need_face_followup, &need_lpr_followup
);
}
if (CTRL_MODEL_ON(m, NETWORK_FIRE)){
read_and_collect_for(
ch, FIRE_NETWORK, "FIRE",
&ts_ns_latest, &total_items, &total_items_cnt, &need_face_followup, &need_lpr_followup
);
}
bool need_face = need_face_followup;
bool need_lpr = need_lpr_followup;
dispatch_stage2_followups(m, ch, rgbdata_for_cnn, npu_input_info.rgb_size, time_stamp_us, need_face, need_lpr);
if (CTRL_MODEL_ON(m, NETWORK_FACE) && need_face){
read_and_collect_for(
ch, FACE_NETWORK, "FACE",
&ts_ns_latest, &total_items, &total_items_cnt, &need_face_followup, &need_lpr_followup
);
}
if (CTRL_MODEL_ON(m, NETWORK_LPR) && need_lpr){
read_and_collect_for(
ch, LPR_NETWORK, "LPR",
&ts_ns_latest, &total_items, &total_items_cnt, &need_face_followup, &need_lpr_followup
);
}
// bool need_lpr = false;
// bool need_abnorm = need_face_followup;
// send_face_followup_if_needed(
// need_followup, ch, networkOrder, rgbdata_for_cnn, npu_input_info.rgb_size
// );
// if (need_face_followup) {
// read_and_collect_for(
// ch, FACE_NETWORK, "FACE",
// &ts_ns_latest, &total_items, &total_items_cnt, &need_followup
// );
// }
uds_det_entry_t dummy;
uint64_t det_ts_us = time_stamp_us;
if (ts_ns_latest > 0) {
det_ts_us = ts_ns_latest / 1000ULL;
}
uds_det_entry_t *send_items =
(total_items_cnt > 0) ? total_items : &dummy;
uint32_t send_cnt = total_items_cnt;
uint32_t seq = ch_seq[ch]++;
uint64_t t0 = now_us();
uds_send_dets_min(ch, seq, det_ts_us, send_items, send_cnt);
printf("[SEND][DET] took %llu us (cnt=%u)\n", (unsigned long long)(now_us() - t0), send_cnt);
if (total_items) free(total_items);
if (fps_win_start_us > 0 && (now_us() - fps_win_start_us) >= 1000000ULL) {
printf("[FPS] ");
for (uint32_t c = 0; c < VIDEO_MAX_CH; c++) {
if (v4l2_config[c].video_buf.video_fd >= 0) {
printf("ch%u=%u ", c, fra_sec_cnt[c]);
}
}
printf("| total=%u fps\n", fra_sec_cnt_total);
// reset window
memset(fra_sec_cnt, 0, sizeof(fra_sec_cnt));
fra_sec_cnt_total = 0;
fps_win_start_us = now_us();
}
}
}
for(int i =0; i< task_cnt; i++) {
pthread_join(p_thread[i], (void **)&status);
}
#ifdef USE_8MP_VI
dsr_deinit();
#endif
uds_server_stop(UDS_SOCK_PATH);
free(rgbdata_for_cnn);
for (int i = 0; i < VIDEO_MAX_CH; i++) {
if (v4l2_config[i].video_buf.video_fd >= 0) {
close(v4l2_config[i].video_buf.video_fd);
v4l2_config[i].video_buf.video_fd = -1;
}
}
return 0;
}